Shell 脚本编程

同传统的编程语言一样,shell 提供了很多特性,这些特性可以使你的 shell 脚本编程更为有用。

创建 Shell 脚本

一个 shell 脚本通常包含如下部分:首行+注释+内容

  • 首行

第一行内容在脚本的首行左侧,表示脚本将要调用的shell解释器,内容如下:

#!/bin/bash

#!符号能够被内核识别成是一个脚本的开始,这一行必须位于脚本的首行,/bin/bash 是 bash 程序的绝对路径,在这里表示后续的内容将通过 bash 程序解释执行。

  • 注释

注释符号 # 放在需注释内容的前面,如下:

#! /bin/bash
# my first shell script(注释)
  • 内容

可执行内容和 shell 结构

#! /bin/bash
echo "hello,world"

Shell 脚本的权限

一般情况下,默认创建的脚本是没有执行权限的

# ll | grep bash
-rw-r--r--. 1 root root      56 11月  1 07:19 bash.sh

没有权限不能执行,需要赋予可执行权限

# chmod +x bash.sh
# ll | grep bash
-rwxr-xr-x. 1 root root      56 11月  1 07:19 bash.sh

Shell 脚本的三种执行方式

  • 1、输入脚本的绝对路径或相对路径

    注:相对路径不能省略 ./

  • 2、bash (或 sh) + 脚本

    注:当脚本没有 x 权限时,root 和文件所有者通过该方式可以正常执行

    注:创建 bash 新环境并执行脚本,执行完释放 bash 环境

  • **3、在脚本的路径前再加 ". " 或 source **

    注:第一种和第二种会创建一个 bash 环境,不同 bash 中的变量无法共享;但是第三种方式是在同一个 bash 里面执行的

Shell 变量

  • 变量:是 shell 传递数据的一种方式,用来代表每个取值的符号名;当 shell 脚本需要保存一些信息时,如一个文件名或是一个数字,就把它存放在一个变量中

  • 变量设置规则:

    1. 变量名称可以由字母,数字和下划线组成,但是不能以数字开头,环境变量名建议大写,便于区分

    2. 在 bash 中,变量的默认类型都是字符串型,如果要进行数值运算,则必须指定变量类型为数值型。

    3. 变量用等号连接值,等号左右两侧不能有空格

  1. 变量的值如果有空格,需要使用单引号或者双引号包括。

变量分类

Linux Shell 的变量分为用户自定义变量、环境变量、位置参数变量和预定义变量

# set # 可以查看系统中存在的所有变量
  • 系统变量:保存和系统操作环境相关的数据,如 $HOME、$PWD、$SHELL、$USER 等

  • 位置参数变量:主要用来向脚本中传递参数或数据,变量名不能自定义,变量作用固定

  • 预定义变量:是 Bash 中已经定义好的变量,变量名不能自定义,变量作用也是固定的

用户自定义变量

用户自定义的变量由字母或下划线开头,由字母,数字或下划线序列组成,并且大小写字母意义不同,变量名长度没有限制。

  • 设置变量

    习惯上用大写字母来命名变量,变量名以字母表示的字符开头,不能用数字。

    # name=zs
    
  • 变量调用

    在使用变量时,要在变量名前加上前缀 “$”
    使用 echo 命令查看变量值。

# echo $name
  • 变量赋值
    1. 定义变量时赋值:变量=值
# STR="hello world"# A=9

​ 2. 将一个命令的执行结果赋给变量

# A=`ls -la`  # 反引号,运行里面的命令,并把结果返回给变量A# A=$(ls -la) # 等价于反引号# echo $A# aa=$((4+5))

​ 3. 将一个变量赋给另一个变量

 # A=$STR
  • 变量叠加
# aa=123# cc="$aa"456  # 使用双引号# cc=${aa}456  # 使用{}# cc='$aa'456  # 无法叠加变量

单引号和双引号的区别:

  • 双引号:双引号里的变量名会替换为变量值

  • 单引号:会将所有特殊字符脱意,里面的内容会原样全部输出

# NUM=10   # SUM="$NUM hehe"  # echo $SUM   10 hehe# SUM='$NUM hehe'  # echo $SUM   $NUM hehe
  • 列出所有的变量

    # set 
    
  • 删除变量

    # aa=22# echo $aa# unset aa# set# echo $aa# readonly bb=2 # 声明静态的变量不能unset# unset bb-bash: unset: bb: cannot unset: readonly variable
    

作用域:用户自定义的变量,作用域为当前的 shell 环境。

环境变量

用户自定义变量只在当前的 shell 中生效,而环境变量会在当前 shell 和其所有子 shell 中生效。如果把环境变量写入相应的配置文件,那么这个环境变量就会在所有的 shell 中生效。

export 变量名=变量值 # 申明环境变量

作用域:当前 shell 以及所有的子 shell

位置参数变量

变量 描述
$n n为数字,$0代表命令本身,$1-$9代表第一到第9个参数, 十以上的参数需要用大括号包含,如${10}。
$* 代表命令行中所有的参数,把所有的参数看成一个整体。以"$1 $2 … $n"的形式输出所有参数
$@ 代表命令行中的所有参数,把每个参数区分对待。以"$1" "$2" … "$n" 的形式输出所有参数
$# 代表命令行中所有参数的个数。添加到shell的参数个数
  • shift 指令

    参数左移,每执行一次,参数序列顺次左移一个位置,$# 的值减1,用于分别处理每个参数,移出去的参数不再可用

    $ 和 $@ 的区别*

    $* 和 $@ 都表示传递给函数或脚本的所有参数

    • 不被双引号" "包含时,都以"$1" "$2" … "$n" 的形式输出所有参数
    • 被双引号" "包含时
      • "$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式输出所有参数
      • "$@" 会将各个参数分开,以"$1" "$2" … "$n" 的形式输出所有参数

预定义变量

变量 描述
$? 执行上一个命令的返回值 执行成功,返回0,执行失败,返回非0(具体数字由命令决定)
$$ 当前进程的进程号(PID),即当前脚本执行时生成的进程号
$! 后台运行的最后一个进程的进程号(PID),最近一个被放入后台执行的进程 &(后台运行)

read 命令

read [选项] 值

read -p(提示语句) -n(字符个数) -t(等待时间,单位为秒) –s(隐藏输入)

运算符

// 无法正确运算(变量默认是字符串类型)# num1=11# num2=22# sum=$num1+$num2 # echo $sum

格式 : expr m + n 或 $((m+n))

expr 命令:对整数型变量进行算术运算

# expr 3 + 5  // expr 运算符间要有空格# expr 3 – 5# echo `expr 10 / 3`# expr 3 \* 10   // \ 是转义符

计算(2 +3 )× 4 的值

# S=`expr 2 + 3`# expr $S \* 4# expr `expr 2 + 3` \* 4# echo $(((2 + 3) * 4)) //推荐使用

$() 、${} 与 $(()) 的区别

  • $( ):用途和反引号``一样,用来表示优先执行的命令(与反引号可读性更高)

    # echo $(ls -l)
    
  • ${ } :获取变量值 (与$相比变量更容易识别)

    # echo ${PATH}
    
  • $((运算内容)) :适用于数值运算(比expr更简洁)

    # echo $((3+1*4))
    

条件测试

内置 test 命令

test 命令可用于测试表达式,支持测试的范围包括:字符串比较,算术比较,文件存在性、属性、类型等判断。例如,判断文件是否为空、文件是否存在、是否是目录、变量是否大于5、字符串是否等于"longshuai"、字符串是否为空等等。在shell 中,几乎所有的判断都使用 test 命令实现。

内置 test 命令常用操作符号有两种格式:

  • test expression
  • [ expression ] # expression表达式首尾都必须要有空格(推荐使用)

表达式的结果为真,则 test 的返回值为 0,否则为非 0

当表达式的结果为真时,则变量$?的值就为 0,否则为非 0

字符串测试:

  • test str1 == str2 或 [ str1 == str2 ] #测试字符串是否相等 =
  • test str1 != str2 或 [ str1 != str2 ] #测试字符串是否不相等
  • test str1 或 [ str1 ] #测试字符串是否不为空,(不为空:true,为空:false)
  • test -n str1 或 [ -n str1 ] #测试字符串是否不为空
  • test -z str1 或 [ -z str1 ] #测试字符串是否为空

注意:程序世界中一般都用1表示true,0表示false。但0表示失败的话,没有具体详细的失败含义,比如失败原因可能是输入参数不合法,可能是数据不存在等等,使用0来涵盖这些所有的异常原因不利于问题排查。

因此在 Bash 中,用 0 表示 true,非0 表示 false。我们可以使用1-255中的任何一个数字来表示某个具体的错误,1是一个普遍的错误,126意味着一个文件不能被执行,127意味着’找不到命令’等。

; 命令连接符号,可以连接多个命令一起执行

  • && 逻辑与条件满足,才执行后面的语句
# [ "hello" == "hello" ] && echo yes
  • || 逻辑或,条件不满足,才执行后面的语句
[ "hello" == "world" ] && echo yes || echo no

整数测试:

符号 运算符
-eq ==
-ge >=
-gt >
-le <=
-lt <
-ne !=
  • test int1 -eq int2 或 [ int1 -eq int2 ] # 测试整数是否相等 equals
  • test int1 -ge int2 或 [ int1-ge int2 ] # 测试int1是否>=int2
  • test int1 -gt int2 或 [ int1 -gt int2 ] # 测试int1是否>int2
  • test int1 -le int2 或 [ int1 -le int2 ] # 测试int1是否<=int2
  • test int1 -lt int2 或[ int1 -lt int2 ] # 测试int1是否<int2
  • test int1 -ne int2 或 [ int1 -ne int2 ] # 测试整数是否不相等
# test 100 –ge 100;echo $?# [ 100 -ge 100 ];echo $?

文件测试:

  • test -d file 或 [ -d file ] # 指定文件是否目录
  • test –e file 或 [ -e file ] # 文件是否存在 exists
  • test -f file 或 [ -f file ] # 指定文件是否常规文件
  • test –L file [ -L file ] # 文件存在并且是一个符号链接
  • test -r file [ -r file ] # 指定文件是否可读
  • test -w file [ -w file ] # 指定文件是否可写
  • test -x file [ -x file ] # 指定文件是否可执行
# test -d install.log  #是否为目录# test –r install.log  #文件是否可读# test –f xx.log ; echo $? #是否是常规文件# [ -L service.soft ] && echo “is a link” #文件存在并且是一个符号链接# test -L /bin/sh ;echo $? #文件存在并且是一个符号链接# [ -f /root ] && echo “yes” || echo “no” #是否是常规文件

多重条件测试:

  • 条件1 –a 条件2 (逻辑与:两个都成立,则为真)
  • 条件1 –o 条件2 (逻辑或 :只要有一个为真,则为真)
  • !条件 (逻辑非:取反)

注意与 && 和 || 的区别,不要混淆在一起

 num=100 [ $num -gt 50 -a $num -gt 100 ];echo $? [ $num -gt 50 -o $num -gt 10 ];echo $?

流程控制语句

if/else命令

1, 单分支if条件语句

if [ 条件判断式 ]  then    代码块fi

或者

if [ 条件判断式 ] ; then   代码块fi
#!/bin/shif [ $USER == "root" ];then  echo "super man"fi

单分支条件语句注意事项:

  • if 语句使用 fi 结尾,和一般语言使用大括号结尾不同

  • [ 条件判断式 ] 就是使用 test 命令判断,所以中括号和条件判断式之间必须有空格

2,多分支if条件语句

if [ 条件判断式1 ]  then   当条件判断式1成立时,执行程序1elif  [ 条件判断式2 ]   then      当条件判断式2成立时,执行程序2...省略更多条件else  当所有条件都不成立时,最后执行此程序fi

case 命令

case 命令是一个多分支的 if/else命令,case 变量的值用来匹配 value1,value2,value3 等等。匹配到后则执行跟在后面的命令直到遇到双分号为止(;😉,case 命令以 esac 作为终止符。

for 循环

for 循环命令用来在一个列表条目中执行有限次数的命令。比如,你可能会在一个姓名列表或文件列表中循环执行同个命令。

  • for 命令后紧跟一个自定义变量、一个关键字 in 和一个字符串列表(可以是变量)
  • 第一次执行 for 循环时,字符串列表中的第一个字符串会赋值给自定义变量,然后执行循环命令,直到遇到done语句;
  • 第二次执行 for 循环时,会右推字符串列表中的第二个字符串给自定义变量,依次类推,直到字符串列表遍历完。

for 语法格式

  • 第一种:
#!/bin/bashfor i in 1 2 3 do echo $idone
  • 第二种:
for ((i = 0; i <= 5; i++))do echo "welcome $i times"done

while 循环

while 命令根据紧跟其后的表达式来判断是否执行 while 循环,当 表达式执行后的返回值为0时,则执行 while 循环语句块,直到遇到 done 语句,然后再返回到 while 命令,判断表达式的返回值,当得打返回值为非0时,则终止 while 循环。

while 语法格式

  • 第一种(循环条件)
while expressiondo	代码done
  • 第二种(死循环)
while :do	代码done

自定义函数

函数代表着一个或一组命令的集合,表示一个功能模块,常用于模块化编程。

以下是关于函数的一些重要说明:

  • 在 shell 中,函数必须先定义再调用

  • 使用 return value 来获取函数的返回值

  • 函数在当前 shell 中执行,可以使用脚本中的变量

函数定义格式

function 函数名(){	命令1….	命令2….	return 返回值变量}

注意:

  • 如果函数名后没有(),在函数名和 { 之间,必须要有空格以示区分

  • 函数返回值,只能通过$? 系统变量获得,可以显示加:return 返回值,如果不加将以最后一条命令运行结果,作为返回值

脚本调试

  • sh -x script:这将执行该脚本并显示所有变量的值。
  • set -x :在shell脚本里添加,对部分脚本调试
  • sh -n script:不执行脚本只是检查语法的模式,将返回所有语法错误。
  • sh –v script:执行并显示脚本内容

数组

定义一个数组

[root@node-01 shell]# array=("zhangsan" "lisi" "wangwu")
打印出数组中所有的字符串
[root@node-01 shell]# echo ${array[@]}
zhangsan lisi wangwu

[root@node-01 shell]# echo ${array[*]}
zhangsan lisi wangwu

打印出数组参数的个数

[root@node-01 shell]# echo ${#array[*]}
3
[root@node-01 shell]# echo ${#array[@]}
3
在数组下标为3的位置插入元素
[root@node-01 shell]# array[3]="zhao"

再次打印数组元素的个数
[root@node-01 shell]# echo ${#array[@]}
4

清除数组下标为0的元素

[root@node-01 shell]# unset array[0]

再次打印数组元素的个数

[root@node-01 shell]# echo ${#array[@]}
3

清除数组0下标元素,下标0位置存在,无元素

[root@node-01 shell]# echo ${array[0]}

posted @ 2021-11-03 23:39  追こするれい的人  阅读(169)  评论(0编辑  收藏  举报