Linux云计算 |【第二阶段】SHELL-DAY3

主要内容:

case语法结构及特点(对Nginx的应用示例)、函数及中断控制(break、continue、exit)、字符串处理(截取、替换、删除、变量初始值定义)

  • 补充:grep选项[-q]功能,不输出查询结果,类似&> /dev/null
  • 补充:wait命令的作用是等待所有后台进程都结束才结束脚本

一、case分支结构

case 分支结构是一种在编程中常用的控制结构,用于根据变量的值执行不同的代码块。

特点:case分支属于匹配执行的方式,它针对指定的变量预先设置一个可能的取值,判断该变量的实际取值是否与预设的某一个值相匹配,如果匹配上了,就执行相应的一组操作;如果没有任何值能够匹配,就执行预先设置的默认命令序列(检查变量的实际取值,如果与预设的值相匹配,则执行对应的操作)

case分支的执行流程

  • 语法格式:
bash 复制代码
#!/bin/bash

# 提示用户输入
echo "Enter a value:"
read input_value

# 使用 case 语句进行分支选择
case $input_value in
    pattern1)
        # 匹配 pattern1 时执行的代码
        echo "Input matches pattern1";;
    pattern2)
        # 匹配 pattern2 时执行的代码
        echo "Input matches pattern2";;
    pattern3|pattern4)
        # 匹配 pattern3 或 pattern4 时执行的代码
        echo "Input matches pattern3 or pattern4";;
    *)
        # 默认情况,即不匹配上述任何模式时执行的代码
        echo "Input does not match any pattern";;
esac

解释说明:

echo "Enter a value:": //提示用户输入一个值

read input_value: //读取用户输入并存储在变量 input_value 中

case $input_value in: //开始 case 语句,根据 input_value 的值进行分支选择

pattern1): //如果 input_value 匹配 pattern1,则执行相应代码块

pattern2): //如果 input_value 匹配 pattern2,则执行相应代码块

pattern3|pattern4): //如果 input_value 匹配 pattern3 或 pattern4,则执行相应代码块

*): //默认情况,即 input_value 不匹配上述任何模式时执行代码块
;;: //每个分支的代码块结束时使用 ;; 表示结束
esac: //case 语句的结束标志

示例:

bash 复制代码
#!/bin/bash

# 提示用户输入
echo "Enter a value:"
read input_value

# 使用 case 语句进行分支选择
case $input_value in
    pattern1)   # 匹配 pattern1 时执行的代码
        echo "Input matches pattern1";;
    pattern2)   # 匹配 pattern2 时执行的代码
        echo "Input matches pattern2";;
    pattern3|pattern4)   # 匹配 pattern3 或 pattern4 时执行的代码
        echo "Input matches pattern3 or pattern4";;
    *)   # 默认情况,即不匹配上述任何模式时执行的代码
        echo "Input does not match any pattern";;
esac

例如:

bash 复制代码
[root@svr7 opt]# vim test03.sh
#!/bin/bash
case $1 in
1)
  echo abc;;
2)
  echo xyz;;
*)
  echo "a|b"
esac

# 测试:
[root@svr7 opt]# bash test03.sh 1
abc
[root@svr7 opt]# bash test03.sh 2
xyz
[root@svr7 opt]# bash test03.sh 3
a|b

例如:

bash 复制代码
[root@svr7 opt]# vim test04.sh
#!/bin/bash
case $1 in
t)
  touch $2;;
m)
  mkdir $2;;
r)
  rm -rf $2;;
*)
  echo "t|m|r"
esac

# 测试:
[root@svr7 opt]# bash test04.sh t AJ.txt
[root@svr7 opt]# ls
AJ.txt
[root@svr7 opt]# bash test04.sh m AnJ
[root@svr7 opt]# ls
AJ.txt  AnJ
[root@svr7 opt]# bash test04.sh r AnJ/
[root@svr7 opt]# ls
AJ.txt
[root@svr7 opt]# bash test04.sh zz
t|m|r

案例:脚本要求能使用redhat、fedora作为控制参数,控制参数通过位置变量$1传入:

  • 当用户输入redhat参数,脚本返回fedora;
  • 当用户输入fedora参数,脚本返回redhat;
  • 当用户输入其他参数,则提示错误信息;
bash 复制代码
[root@svr5 ~]# vim test.sh
#!/bin/bash
case $1 in
redhat)
    echo "fedora";;
fedora)
    echo "redhat";;
*)                     //默认输出脚本用法
    echo "用法: $0 {redhat|fedora}"
esac

# 测试:
[root@svr5 ~]# chmod +x test.sh
[root@svr5 ~]# ./test.sh     //未提供参数,或提供参数无法识别时,提示正确用法
用法: ./test.sh {redhat|fedora}
[root@svr5 ~]# ./test.sh redhat    //确认脚本可以响应redhat控制参数
fedora
[root@svr5 ~]# ./test.sh fedora    //确认脚本可以响应fedora控制参数
redhat

案例:编写case脚本实现一键启停Nginx服务器

源码安装Nginx需要提前安装依赖包软件:gcc,make,openssl-devel,pcre-devel

步骤1:将lnmp_soft.tar.gz远程拷贝到工作机192.168.4.7

bash 复制代码
[root@localhost 桌面]# cd /linux-soft/2/
[root@localhost 2]# scp lnmp_soft.tar.gz root@192.168.4.7:/
root@192.168.4.7's password:
lnmp_soft.tar.gz                    100%   87MB 159.3MB/s   00:00   

步骤2:工作机检查lnmp_soft.tar.gz并拷贝到/opt目录下

bash 复制代码
[root@svr7 /]# tar -xf lnmp_soft.tar.gz
[root@svr7 /]# ls lnmp_soft
apache-tomcat-8.0.30.tar.gz  nginx-1.17.6.tar.gz   vpn
...
[root@svr7 /]# cp lnmp_soft/nginx-1.17.6.tar.gz /opt/
[root@svr7 /]# ls /opt/
nginx-1.17.6.tar.gz

步骤3:编写脚本内容:

bash 复制代码
[root@svr7 opt]# vim test05.sh     //编写脚本
#!/bin/bash
yum -y install gcc make pcre-devel openssl-devel    //安装依赖包
tar -xf nginx-1.17.6.tar.gz    //在/opt目录下解压tar包释放源代码
cd nginx-1.17.6     //切换到解压后的目录
./configure     //配置,不指定安装目录则当前解压后目录配置
make    //编译
make install    //安装
[root@svr7 opt]# bash test05.sh    //执行源码安装编译脚本

Nginx默认安装路径为/usr/local/nginx,该目录下会提供4个子目录,分别如下:

  • /usr/local/nginx/conf //配置文件目录
  • /usr/local/nginx/html //网站页面目录
  • /usr/local/nginx/logs //Nginx日志目录(Nginx程序运行时会开启)
  • /usr/local/nginx/sbin //主程序目录
bash 复制代码
[root@svr7 opt]# ls /usr/local/nginx/
conf  html  logs  sbin
[root@svr7 opt]# systemctl stop httpd   //停止httpd
[root@svr7 opt]# /usr/local/nginx/sbin/nginx   //手动执行,源码编译安装无法使用systemctl
[root@svr7 opt]# curl 192.168.4.7
...
<title>Welcome to nginx!</title>     //显示Welcome表示显示成功
...

注意:因Nginx也占用80端口,为了避免服务冲突,建议将其他带80端口服务暂停

bash 复制代码
[root@svr7 opt]# /usr/local/nginx/sbin/nginx    //手动执行nginx
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()
...

主程序命令参数:

bash 复制代码
[root@svr5 ~]# /usr/local/nginx/sbin/nginx           //启动服务
[root@svr5 ~]# /usr/local/nginx/sbin/nginx -s stop   //关闭服务
[root@svr5 ~]# /usr/local/nginx/sbin/nginx -V        //查看软件信息

步骤4:编写case脚本,通过位置变量$1读取用户的操作指令,判断start、stop、restart、status

bash 复制代码
[root@svr7 opt]# vim test06.sh
#!/bin/bash
case $1 in
start)
   /usr/local/nginx/sbin/nginx;;
stop)
   /usr/local/nginx/sbin/nginx -s stop;;
restart)
   /usr/local/nginx/sbin/nginx -s stop
   /usr/local/nginx/sbin/nginx;;    //注意每个模式最后一条命令序列。需【;;】双分号结尾
status)
   netstat -ntulp | grep -q nginx    //[-q]选项的功能类似&> /dev/null
   if [ $? -eq 0 ];then
      echo "服务已启动"
   else
      echo "服务未启动"
   fi;;
*)
   echo "Error,输入指令错误"
esac

# 测试:
[root@svr7 opt]# bash test06.sh start
[root@svr7 opt]# bash test06.sh status
服务已启动
[root@svr7 opt]# bash test06.sh stop
[root@svr7 opt]# bash test06.sh status
服务未启动

**补充:**netstat命令可以查看系统中启动的端口信息,该命令常用选项如下:

  • [-n] 以数字格式显示端口号
  • [-t] 显示TCP连接的端口
  • [-u] 显示UDP连接的端口
  • [-l] 显示服务正在监听的端口信息,如httpd启动后,会一直监听80端口
  • [-p] 显示监听端口的服务名称是什么(也就是程序名称)
bash 复制代码
[root@svr7 opt]# netstat -ntulp | grep :80     //或者"grep nginx"
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      10500/nginx: master

二、Shell函数

Shell函数是一种在Shell脚本中定义和使用可重用代码块的方法。通过使用函数,你可以将复杂的任务分解为更小的、更易于管理的部分,从而提高脚本的可读性和可维护性。

特点:将一些需要重复使用的操作定义为公共的语句块,即可称为函数;使脚本代码更简洁,增强易读性,提高Shell脚本的执行效率;

函数适用于比较复杂的启动/终止控制操作,方便在需要时多次调用;

1、函数定义

函数可以通过两种方式定义:

  • 语法格式1:
bash 复制代码
function function_name {
    # 函数体
}
  • 语法格式2:
bash 复制代码
function_name() {
    # 函数体
}

2、调用函数

调用函数非常简单,只需使用函数名即可:

  • 语法格式:
bash 复制代码
function_name

函数需先定义才能调用,好比脚本的"内部命令"

回忆:特殊变量

  • \$0:脚本名。
  • \$1, \$2, ...:第1个、第2个参数等。
  • $#:传递给脚本的参数个数。
  • $*:所有参数作为一个字符串。
  • $@:所有参数作为单独的字符串。
  • :当前Shell进程的PID。


示例:简单的问候语

bash 复制代码
#!/bin/bash

# 定义一个函数
greet() {
    echo "Hello, \$1!"
}

# 调用函数并传递参数
greet "Alice"   # 输出Hello, Alice
greet "Bob"     # 输出Hello, Bob

greet函数定义了一个简单的问候语,并接受一个参数\$1,这是传递给函数的第一个参数。

在调用greet函数时,传递了字符串"Alice"和"Bob"作为参数。

示例:计算两个数的和

bash 复制代码
#!/bin/bash

# 定义一个函数来计算两个数的和
sum() {
    local result=$(( \$1 + \$2 ))
    echo $result
}

# 调用函数并捕获输出
result=$(sum 5 3)
echo "The sum is: $result"    # 输出:The sum is: 8

sum函数计算两个数的和,并通过echo输出结果。

通过命令替换$(sum 5 3)捕获函数的输出,并将其存储在变量result中。

示例:局部变量

bash 复制代码
#!/bin/bash

# 定义一个函数
example() {
    local local_var="I'm local"
    global_var="I'm global"
    echo $local_var
    echo $global_var
}

# 调用函数
example

# 尝试访问局部变量和全局变量
echo $local_var  # 输出为空,因为local_var是局部变量
echo $global_var # 输出 "I'm global"

在函数内部,可以使用local关键字定义局部变量,这些变量只在函数内部可见,不会影响全局变量:

例如:

bash 复制代码
[root@svr7 ~]# mycd(){      //定义函数(换行直接在{进行空格)
> mkdir /test
> cd /test
> }
[root@svr7 ~]# mycd        //调用函数
[root@svr7 test]#

3、函数传值

在Shell脚本中,函数可以通过位置参数来传递值。位置参数是指在调用函数时传递给函数的参数,它们可以通过特殊的变量(如\$1, \$2, \$3, ...)在函数内部访问。以下是一些关于如何在Shell函数中传递和使用参数的详细示例和解释。

示例:传递和使用参数

bash 复制代码
#!/bin/bash

# 定义一个函数,接受两个参数并计算它们的和
sum() {
    local num1=\$1
    local num2=\$2
    local result=$((num1 + num2))
    echo $result
}

# 调用函数并传递参数
result=$(sum 5 3)
echo "The sum is: $result"

sum函数定义了两个局部变量num1和num2,它们分别接收传递给函数的第一个和第二个参数(\$1和\$2)。

函数计算这两个数的和,并通过echo输出结果。

在调用sum函数时,传递了两个参数5和3。

通过命令替换$(sum 5 3)捕获函数的输出,并将其存储在变量result中。

示例:传递多个参数

bash 复制代码
#!/bin/bash

# 定义一个函数,接受任意数量的参数并打印它们
print_args() {
    for arg in "$@"; do
        echo "Argument: $arg"
    done
}

# 调用函数并传递多个参数
print_args "Hello" "World" 1 2 3

print_args函数使用"$@"来访问所有传递给函数的参数。

通过for循环遍历每个参数,并打印它们的值。

在调用print_args函数时,传递了多个参数"Hello", "World", 1, 2, 3。

案例:结合echo -e 并为函数传值

echo选项[-e]相当于扩展功能。可以激活后面特殊字符的作用;

\033[31m 代表设置颜色为红色,ABCD是输入内容,\033[0m代表还原颜色

bash 复制代码
[root@svr7 opt]# echo -e "\033[31mABCD\033[0m"    //\033[0m还原颜色
ABCD
[root@svr7 opt]# echo -e "\033[32mABCD\033[0m"
ABCD

如图所示:

例如:编写函数脚本

bash 复制代码
[root@svr7 opt]# vim test07.sh
#!/bin/bash
color(){
 echo -e "\033[$1m$2\033[0m"
}
color 31 ABCD
color 32 VIVO
color 33 UBER
color 34 OPPO
[root@svr7 opt]# bash test07.sh

如图所示:

例如:编写函数脚本

正常报错:

bash 复制代码
① [root@svr7 opt]# bash test06.sh stop    //重复停止Nginx服务报错
nginx: [error] open() "/usr/local/nginx/logs/nginx.pid" failed (2: No such file or directory)
② [root@svr7 opt]# bash test06.sh start   //重复开启Nginx服务报错
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()
...
bash 复制代码
[root@svr7 opt]# vim test06.sh
#!/bin/bash
color(){         //定义函数,增加函数改变颜色
 echo -e "\033[$1m$2\033[0m"
}
 
case $1 in
start)
   netstat -ntulp | grep -q nginx && echo "服务已经开启" && exit   //netstat检测到服务开启,则输出信息并退出,不会重复开启;
   /usr/local/nginx/sbin/nginx;;
stop)
   [ ! -e /usr/local/nginx/logs/nginx.pid ] && echo "服务已经关闭" && exit  //条件判断服务文件不存在,则表示服务未运行并退出,不会重复关闭;
   /usr/local/nginx/sbin/nginx -s stop;;
restart)
   /usr/local/nginx/sbin/nginx -s stop
   /usr/local/nginx/sbin/nginx;;
status)
   netstat -ntulp | grep -q nginx
   [ $? -eq 0 ] && color 32 "服务已启动" || color 31 "服务未启动";;   //改变服务颜色
*)   
   color 33 "Error,输入指令错误";;
esac

如图所示:

三、Shell中的中断、继续和退出

在Shell编程中,中断、继续和退出是控制流程的重要概念,它们分别对应于不同的命令和操作。

  • [ break ] 可以终止循环,继续循环之后的任务;
  • [ continue ] 终止当前循环,继续下一次循环;
  • [ exit ] 可以终止循环,但同时也终止脚本;

1、中断(Break)

break命令用于立即终止当前循环(如for、while或until循环)的执行,并跳出循环体。break通常用于在满足某个条件时提前结束循环

bash 复制代码
#!/bin/bash

for i in {1..10}; do
    if [ $i -eq 5 ]; then
        echo "Breaking at $i"
        break
    fi
    echo "Number: $i"
done

在这个示例中,当i等于5时,break命令被执行,循环立即终止,不再继续执行后续的迭代。

2、继续(Continue)

continue命令用于跳过当前循环迭代的剩余部分,并继续下一次迭代。continue通常用于在满足某个条件时跳过当前迭代的某些处理步骤。

bash 复制代码
#!/bin/bash

for i in {1..10}; do
    if [ $i -eq 5 ]; then
        echo "Skipping $i"
        continue
    fi
    echo "Number: $i"
done

在这个示例中,当i等于5时,continue命令被执行,当前迭代的剩余部分被跳过,直接进入下一次迭代。

3、退出(Exit)

exit命令用于立即终止Shell脚本的执行,并返回一个可选的退出状态码。exit通常用于在满足某个条件时提前结束整个脚本的执行。

bash 复制代码
#!/bin/bash

for i in {1..10}; do
    if [ $i -eq 5 ]; then
        echo "Exiting at $i"
        exit 1
    fi
    echo "Number: $i"
done

echo "This line will not be executed if exit is called."

在这个示例中,当i等于5时,exit 1命令被执行,脚本立即终止,不再执行后续的任何代码。1是退出状态码,表示脚本以非零状态退出,通常用于表示错误或异常情况。

例如1:break

bash 复制代码
[root@svr7 test]# vim test10.sh
#!/bin/bash
for i in {1..5}
do
   [ $i -eq 3 ] && break    //当变量等于3,则终止循环,继续循环之后的任务;
   echo $i
done
echo "GAME OVER"

# 验证
[root@svr7 test]# bash test10.sh
1
2
GAME OVER

例如2:continue

bash 复制代码
[root@svr7 test]# vim test10.sh
#!/bin/bash
for i in {1..5}
do
   [ $i -eq 3 ] && continue    //当变量等于3,则跳出当前循环,继续下一次循环;
   echo $i
done
echo "GAME OVER"

# 验证
[root@svr7 test]# bash test10.sh
1
2
4
5
GAME OVER

例如3:exit

bash 复制代码
[root@svr7 test]# vim test10.sh
#!/bin/bash
for i in {1..5}
do
   [ $i -eq 3 ] && exit     //当变量等于3,则终止循环,退出脚本;
   echo $i
done
echo "GAME OVER"

# 验证
[root@svr7 test]# bash test10.sh
1
2

案例1:从键盘循环取整数(0结束)并求和,输出最终结果

bash 复制代码
[root@svr7 opt]# vim test08.sh
#!/bin/bash
SUM=0
while :
   read -p "请输入一个整数求和(0表示结束):" num
do
  [ $num -eq 0 ] && echo "所有整数之和为$SUM" && exit
  let SUM+=num
done

# 测试:
[root@svr7 opt]# bash test08.sh
请输入一个整数求和(0表示结束):3     // 0+3=3
请输入一个整数求和(0表示结束):2     // 3+2=5
请输入一个整数求和(0表示结束):5     // 5+5=10
请输入一个整数求和(0表示结束):0     // 10+0=10
所有整数之和为10

案例2:从键盘循环取整数(0结束)并求和,输出最终结果(优化:非空值)

bash 复制代码
[root@svr7 opt]# vim test08.sh
#!/bin/bash
SUM=0
while :
  read -p "请输入一个整数求和(0表示结束):" num
do
  [ -z $num ] && echo "ERROR:输入为空值" && continue   //判断输入值为空条件
  [ $num -eq 0 ] && break
  let SUM+=num     //自增运算
done
  echo "所有整数之和为$SUM"

# 测试:
[root@svr7 opt]# bash test08.sh
请输入一个整数求和(0表示结束):
ERROR:输入为空值
请输入一个整数求和(0表示结束):2
请输入一个整数求和(0表示结束):3
请输入一个整数求和(0表示结束):0
所有整数之和为5

案例3:找出1-10内6的倍数,并打印它的平方值

bash 复制代码
[root@svr5 ~]# vim test.sh
#!/bin/bash
for i in {1..10}
do
    [ $[i%6] -ne 0 ]  &&  continue
    echo $[i*i]  # 6x6=36
Done

# 测试:
[root@svr7 opt]# bash test.sh
36

四、字符串处理

在Shell脚本中,字符串处理是一个常见的任务,包括字符串的截取、替换、匹配删除以及初值处理。以下是一些常用的方法和示例,帮助你理解和掌握这些操作。

  • Shell提供了 ${} 语法来方便地进行字符串操作

1、字符串截取

字符串截取通常指的是从字符串中提取出一部分子字符串。在Shell中,可以使用以下几种方法来实现字符串截取:

  • 语法格式:${string:position} //从 string 的 position 位置开始截取到最后。
  • 语法格式:${string:position:length} //从 string 的 position 位置开始截取 length 长度的子字符串。

position 的编号默认从0开始(可省略)

示例:

bash 复制代码
#!/bin/bash
string="Hello, World!"

# 从位置7开始截取到最后
echo ${string:7}    # 输出: World!

# 从位置0开始截取5个字符
echo ${string:0:5}  # 输出: Hello

例如:创建一个8位数的随即密码

$RANDOM 用来产生随机数(环境变量)

bash 复制代码
[root@svr7 opt]# echo $RANDOM
44511
[root@svr7 opt]# echo $[RANDOM%62]    //通过随机数(被除数)取62(除数)的余数
43

(补充:有余数的除法中余数一定小于除数,所以余数不会大于除数62)

定义一个变量x,并确认其字符串长度:

bash 复制代码
[root@svr7 opt]# x=qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890
[root@svr7 opt]# echo ${#x}    //查看变量长度
62
bash 复制代码
[root@svr7 opt]# vim test09.sh
#!/bin/bash
x=qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890       //所有密码的可能性是26+26+10=62(0-61是62个数字)
pass=''
for i in {1..8}
do
  n=$[RANDOM%62]    //从0~62中产生随机数n作为截取起始位置
  z=${x:n:1}      //从x变量中截取1个随机数n,定义到变量z中
  pass=${pass}$z    //将每次获取的随机字符存储到变量pass中
done
echo $pass

使用 cut 命令

cut 命令可以用来按字符、字节或字段切割字符串。

  • 语法格式:cut -c start-end:按字符位置截取。

示例:

bash 复制代码
#!/bin/bash
string="Hello, World!"

# 按字符位置截取
echo $string | cut -c 1-5  # 输出: Hello

使用 awk 命令

awk 是一个强大的文本处理工具,可以用来进行复杂的字符串切割。

示例:

bash 复制代码
#!/bin/bash
string="apple,banana,cherry"

# 使用 awk 进行切割
echo $string | awk -F, '{print \$1; print \$2; print \$3}'

2、字符串替换

字符串替换通常指的是将字符串中的某个子字符串替换为另一个子字符串。

  • 语法格式:${string/pattern/replacement} //将 string 中的第一个匹配 pattern 的子字符串替换为 replacement
  • 语法格式:${string//pattern/replacement} //将 string 中所有匹配 pattern 的子字符串替换为 replacement

注意:临时效果,不改变变量的值

示例:

bash 复制代码
#!/bin/bash

string="Hello, World!"

# 替换第一个匹配的 "o" 为 "0"
echo ${string/o/0}    # 输出: Hell0, World!

# 替换所有匹配的 "o" 为 "0"
echo ${string//o/0}   # 输出: Hell0, W0rld!

例如:将定义的Phone字符串中的第1个8替换为X

bash 复制代码
[root@svr7 opt]# phone=13788768897
[root@svr7 opt]# echo $phone
13788768897
[root@svr7 opt]# echo ${phone/8/X}
137X8768897

例如:将定义的phone字符串中的所有8都替换为X

bash 复制代码
[root@svr7 opt]# echo ${phone//8/X}
137XX76XX97

例如:通过字符串替换,将所有的8进行删除(new值给空)

bash 复制代码
[root@svr7 ~]# echo ${phone/8}
1378768897
[root@svr7 ~]# echo ${phone//8}
1377697

例如:字符串的替换不会改变变量的值,只有重新赋值才会改变变量的值

bash 复制代码
[root@svr7 ~]# echo $phone
13788768897
[root@svr7 ~]# phone=${phone//8/}
[root@svr7 ~]# echo $phone
1377697

使用 sed 命令

sed 是一个流编辑器,可以用来进行复杂的字符串替换。

  • sed 's/pattern/replacement/' //将第一个匹配 pattern 的子字符串替换为 replacement
  • sed 's/pattern/replacement/g' //将所有匹配 pattern 的子字符串替换为 replacement

示例:

bash 复制代码
#!/bin/bash

string="Hello, World!"

# 替换第一个匹配的 "o" 为 "0"
echo $string | sed 's/o/0/'    # 输出: Hell0, World!

# 替换所有匹配的 "o" 为 "0"
echo $string | sed 's/o/0/g'   # 输出: Hell0, W0rld!

3、字符串的匹配删除

在Shell脚本中,字符串的匹配删除是一个常见的任务,特别是按条件掐头或去尾。以下是一些常用的方法和示例,帮助你理解和掌握这些操作。

1)按条件掐头

按条件掐头通常指的是从字符串的开头删除匹配某个模式的子字符串

  • 语法格式:${string#pattern} //从 string 的开头删除最短匹配 pattern 的子字符串
  • 语法格式:${string##pattern} //从 string 的开头删除最长匹配 pattern 的子字符串

注意:临时效果,不改变变量的值

示例:

bash 复制代码
#!/bin/bash

string="Hello, World!"

# 从开头删除最短匹配的 "He"
echo ${string#He}    # 输出: llo, World!

# 从开头删除最长匹配的 "He"
echo ${string##He}   # 输出: llo, World!

# 从开头删除最短匹配的 "He" 和 "llo"
echo ${string#He*llo}    # 输出: , World!

# 从开头删除最长匹配的 "He" 和 "llo"
echo ${string##He*llo}   # 输出: , World!

例如:以处理系统默认的账户信息为例,定义变量A

bash 复制代码
[root@svr5 ~]# A=`head -1 /etc/passwd`
[root@svr5 ~]# echo $A
root:x:0:0:root:/root:/bin/bash

从左向右,最短匹配删除
[root@svr7 opt]# echo ${A#*:}
x:0:0:root:/root:/bin/bash

从左向右,最长匹配删除
[root@svr7 opt]# echo ${A##*:}
/bin/bash

2)按条件去尾

按条件去尾通常指的是从字符串的结尾删除匹配某个模式的子字符串

  • ${string%pattern} //从 string 的结尾删除最短匹配 pattern 的子字符串
  • ${string%%pattern} //从 string 的结尾删除最长匹配 pattern 的子字符串

示例:

bash 复制代码
#!/bin/bash

string="Hello, World!"

# 从结尾删除最短匹配的 "d!"
echo ${string%d!}    # 输出: Hello, Worl

# 从结尾删除最长匹配的 "d!"
echo ${string%%d!}   # 输出: Hello, Worl

# 从结尾删除最短匹配的 "rld!"
echo ${string%rld!}    # 输出: Hello, Wo

# 从结尾删除最长匹配的 "rld!"
echo ${string%%rld!}   # 输出: Hello, Wo

例如:以处理系统默认的账户信息为例,定义变量A

bash 复制代码
[root@svr5 ~]# A=`head -1 /etc/passwd`
[root@svr5 ~]# echo $A
root:x:0:0:root:/root:/bin/bash

从右向左,最短匹配删除
[root@svr7 opt]# echo ${A%:*}
root:x:0:0:root:/root

从右向左,最长匹配删除
[root@svr7 opt]# echo ${A%%:*}
root

例如:短匹配删除

bash 复制代码
[root@svr7 ~]# a=abcdefghijk
[root@svr7 ~]# echo ${a#abcd}
efghijk
[root@svr7 ~]# echo ${a%ijk}
abcdefgh

例如:长匹配删除

bash 复制代码
[root@svr7 ~]# a=abcdeabcde
[root@svr7 ~]# echo ${a##*abc}
de
[root@svr7 ~]# echo ${a%%de*}
abc

例如:字符串的匹配删除不会改变变量的值,只有重新赋值才会改变变量的值

bash 复制代码
[root@svr7 ~]# echo $a
abcdefghijk
[root@svr7 ~]# a=${a##abc}
[root@svr7 ~]# echo $a
defghijk

**常见问题:**删除时必须按照顺序,从头从尾的第一个字符串开始

bash 复制代码
[root@svr7 ~]# echo ${a%hi}
abcdefghijk
[root@svr7 ~]# echo ${a#cd}
abcdefghijk

案例:编写脚本,实现批量修改文件扩展名

创建测试文件

bash 复制代码
[root@svr7 opt]# touch abc{01..10}.txt
[root@svr7 opt]# ls
abc01.txt  abc03.txt  abc05.txt  abc07.txt  abc09.txt
abc02.txt  abc04.txt  abc06.txt  abc08.txt  abc10.txt

编写脚本

bash 复制代码
[root@svr7 opt]# vim test01.sh
#!/bin/bash
for i in $(ls *.txt)     //查找以.txt结尾,$()同等于``
do
  n=${i%.*}
  mv $i $n.doc
done
同等于
for i in $(ls *.txt)      //ls *.txt查看当前目录下以.txt结尾的文件
do
  mv $i ${i%.*}.doc
done

--- 验证:
[root@svr7 opt]# bash test01.sh
[root@svr7 opt]# ls
abc01.doc  abc03.doc  abc05.doc  abc07.doc  abc09.doc  test01.sh
abc02.doc  abc04.doc  abc06.doc  abc08.doc  abc10.doc

--- 编写脚本(优化)

bash 复制代码
#!/bin/bash
for i in $(ls *.$1)
do
  n=${i%.*}
  mv $i $n.$2
done

---验证:
[root@svr7 opt]# bash test01.sh doc png
[root@svr7 opt]# ls
abc01.png  abc03.png  abc05.png  abc07.png  abc09.png  test01.sh
abc02.png  abc04.png  abc06.png  abc08.png  abc10.png

--- 编写脚本(优化,read)

bash 复制代码
[root@svr7 opt]# vim test01.sh
#!/bin/bash
read -p "请问您需要改什么文件类型:" file
read -p "请问您需要改成什么文件类型:" file2
for i in $(ls *.$file)
do
  n=${i%.*}
  mv $i $n.$file2
done

---验证:
[root@svr7 opt]# bash test01.sh
请问您需要改什么文件类型:png
请问您需要改成什么文件类型:doc
[root@svr7 opt]# ls
abc01.doc  abc03.doc  abc05.doc  abc07.doc  abc09.doc  test01.sh
abc02.doc  abc04.doc  abc06.doc  abc08.doc  abc10.doc

4、字符串初值的处理

字符串初值的处理通常指的是在字符串为空或未定义时,为其设置一个默认值。在Shell中,可以使用以下方法来实现字符串初值的处理:

  • 语法格式:${string:-default} //如果 string 未定义或为空,则返回 default。

示例:

bash 复制代码
#!/bin/bash

# 未定义变量
echo ${undefined:-"Default Value"}    # 输出: Default Value

# 定义但为空
empty=""
echo ${empty:-"Default Value"}        # 输出: Default Value

# 定义且非空
defined="Hello"
echo ${defined:-"Default Value"}      # 输出: Hello

例如:

bash 复制代码
[root@svr7 opt]# XX=11
[root@svr7 opt]# echo $XX
11
[root@svr7 opt]# echo ${XX:-123}   //因XX的变量值已存在,则输出变量XX的值
11
[root@svr7 opt]# echo ${YY:-123}   //因YY的变量值不存在,输出"123"
123

案例:利用字符串的初值,使创建用户未输入密码,则默认密码为123456

--- 编写脚本

bash 复制代码
[root@svr7 opt]# vim test02.sh
#!/bin/bash
useradd abc01
read -p "请输入密码:" pass
echo ${pass:-123456} | passwd --stdin abc01

# 验证:
[root@svr7 opt]# bash test02.sh
请输入密码:
更改用户 abc01 的密码 。
passwd:所有的身份验证令牌已经成功更新。

--- 编写脚本(优化)

bash 复制代码
[root@svr7 opt]# vim test02.sh
#!/bin/bash
read -p "请输入用户名:" user
[ -z $user ] && exit      //如果无用户名,则脚本退出
read -p "请输入密码:" pass
useradd $user
echo ${pass:-123456} | passwd --stdin $user  //用户未输入密码,默认为123456

--- 验证:
[root@svr7 opt]# bash test02.sh
请输入用户名:Tom
请输入密码:
更改用户 Tom 的密码 。
passwd:所有的身份验证令牌已经成功更新。

案例:从键盘读入一个正整数x,求从1到x的和;当用户未输入值(直接回车)时,为了避免执行出错,应为x赋初值1;

bash 复制代码
[root@svr7 opt]# vim test03.sh
#!/bin/bash
read -p "请输入一个正整数:" x
x=${x:-1}
i=1;SUM=0
while [ $i -le $x ]
do
  let SUM+=i
  let i++
done
echo "从1到$x的总和是:$SUM"

--- 验证:
[root@svr7 opt]# bash test03.sh
请输入一个正整数:3     //输入3,正常读入并计算、输出结果(1+2+3)
从1到3的总和是:6
[root@svr7 opt]# bash test03.sh
请输入一个正整数:4     //输入4,正常读入并计算、输出结果(1+2+3+4)
从1到4的总和是:10
[root@svr7 opt]# bash test03.sh
请输入一个正整数:      //直接回车,设x=1后计算、输出结果
从1到1的总和是:1

小结:

本篇章节为**【第二阶段】SHELL-DAY3**的学习笔记,这篇笔记可以初步了解到 case语法结构及特点、函数及中断控制(break、continue、exit)、字符串处理。


Tip:毕竟两个人的智慧大于一个人的智慧,如果你不理解本章节的内容或需要相关笔记、视频,可私信小安,请不要害羞和回避,可以向他人请教,花点时间直到你真正的理解

相关推荐
A ?Charis40 分钟前
记录一下,Vcenter清理/storage/archive空间
云计算
默子昂2 小时前
yolo自动化项目实例解析(一)日志格式输出、并发异步多线程、websocket、循环截图、yolo推理、3d寻路
运维·yolo·自动化
完球了2 小时前
【Day14-单例设计模式&动态代理】
java·开发语言·笔记·学习·设计模式·学习经验
大数据AI人工智能培训专家培训讲师叶梓2 小时前
大模型从失败中学习 —— 微调大模型以提升Agent性能
人工智能·学习·性能优化·微调·agent·代理·大模型微调
飞翔沫沫情4 小时前
记一次 FastDFS 存储节点迁移:基于 scp 的实践与经验分享
经验分享·docker·fastdfs迁移
秃了也弱了。4 小时前
使用iperf3测试局域网服务器之间带宽
运维
板栗妖怪5 小时前
内网穿透之EW使用、判断服务器是否出网
学习·内网渗透
Hadoop_Liang5 小时前
xshell密钥方式连接阿里云Linux
linux·阿里云·云计算
William.csj6 小时前
Bitvise——进入服务器的快捷方式
运维·服务器
ersaijun7 小时前
【Obsidian】当笔记接入AI,Copilot插件推荐
人工智能·笔记·copilot