shell脚本

学习整理之菜鸟驿站:

一、什么是shell

Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。Shell 又是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。

1.1 shell脚本

Shell 脚本(shell script),是一种为 shell 编写的脚本程序。
shell脚本以#!/bin/bash为开头,#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。。
shell脚本拓展名是sh(如test.sh),运行只要使用 ./test.sh就行。(一定要注意,运行时一定要查看是否又运行权限,如果没有运行权限,可以使用命令 chmod +x ./test.sh

二、shell脚本的书写规则

2.1 shell变量

  1. 命名变量:your_name="runoob.com"
    书写注意点:变量名和等号之间不能有空格。
  2. 使用变量:echo ${your_name}
    注意点说明:1、使用已定义过的变量,只要在变量名前面加美元符号即可;
    2、大括号是可选的,但是最好加一下,方便阅读;
    3、**如果想第二次赋值,不要加$(只有使用时加),直接赋值就好,your_name="tom"
    4、使用命令readonly可以使变量只读,不能被改变,如readonly your_name
  3. 删除变量:unset your_name
  4. 变量类型:
    1、局部变量:局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量
    2、环境变量: 所有的程序,包括shell启动的程序,都能访问环境变量
    3、shell变量:shell变量是由shell程序设置的特殊变量

2.2 shell 字符串

字符串和变量类似,字符串可以用单引号,也可以用双引号,也可以不用引号。

  1. 单引号str='this is a string'
    限制:
    单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
    单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。
  2. 双引号
    优点:双引号里可以有变量;双引号里可以出现转义字符
your_name='runoob'
str="Hello, I know you are \"$your_name\"! \n"
echo -e $str
输出:Hello, I know you are "runoob"! 
  1. 拼接字符串
your_name="runoob"
# 使用双引号拼接
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting  $greeting_1
# 使用单引号拼接
greeting_2='hello, '$your_name' !'
greeting_3='hello, ${your_name} !'
echo $greeting_2  $greeting_3
输出:
hello, runoob ! hello, runoob !
hello, runoob ! hello, ${your_name} !
ps:单引号内不能使用变量。
  1. 获取字符串长度: echo ${#string}
  2. 提取子字符串:echo ${string:1:4}
    PS:第一个字符的索引值为 0
  3. 查找子字符串:下面使用的是反引号 `,不是单引号 '
    查找字符 i 或 o 的位置(哪个字母先出现就计算哪个):
string="runoob is a great site"
echo `expr index "$string" io`  # 输出 4

2.3 shell 传递参数

我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……


shell脚本_第1张图片

特殊字符来处理参数:


| $# | 传递到脚本的参数个数 |
| $* | 以一个单字符串显示所有向脚本传递的参数。
如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。 |
| $$ | 脚本运行的当前进程ID号 |
| $! | 后台运行的最后一个进程的ID号 |
| $@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。
如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。 |
| $- | 显示Shell使用的当前选项,与[set命令](https://www.runoob.com/linux/linux-comm-set.html)功能相同。 |
| $? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |

2.4 shell数组

2.4.1 定义数组

bash仅支持一维数组(不支持多维数组),并且没有限定数组的大小,即初始化时不需要定义数组大小。
类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。

array_name=(value1 value2 ... valuen)
如:my_array=(A B "C" D)
或者
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2

2.4.2 读取数组

${array_name[index]}
注意:数组index是从0开始的,和c语言一样

当想要获取数组中所有的元素时,可以使用@或者 * 来实现,如,

echo "数组的元素为: ${my_array[*]}"
echo "数组的元素为: ${my_array[@]}"

** 当想要获取数组长度时,与获取字符串方法一样,加#号**
echo "${#my_array[*]}"

2.5 shell 运算符

Shell 和其他编程语言一样,支持多种运算符,包括:

  • 算数运算符
  • 关系运算符
  • 布尔运算符
  • 字符串运算符
  • 文件测试运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。
如:
val=`expr 2 + 2`
注意:

  • 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。
  • 完整的表达式要被 包含,注意这个字符不是常用的单引号,在 Esc 键下边。
shell脚本_第2张图片
算术运算符

关系运算符

关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明  举例
-eq 检测两个数是否相等,相等返回 true。    [ $a -eq $b ] 返回 false。
-ne 检测两个数是否不相等,不相等返回 true。  [ $a -ne $b ] 返回 true。
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ] 返回 true。
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。   [ $a -ge $b ] 返回 false。
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。   [ $a -le $b ] 返回 true。
布尔运算符
下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明  举例
!   非运算,表达式为 true 则返回 false,否则返回 true。  [ ! false ] 返回 true。
-o  或运算,有一个表达式为 true 则返回 true。  [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a  与运算,两个表达式都为 true 才返回 true。  [ $a -lt 20 -a $b -gt 100 ] 返回 false。
shell脚本_第3张图片
字符串运算符
下表列出了常用的字符串运算符,假定变量 a 为 "abc",变量 b 为 "efg":

运算符 说明  举例
=   检测两个字符串是否相等,相等返回 true。  [ $a = $b ] 返回 false。
!=  检测两个字符串是否不相等,不相等返回 true。    [ $a != $b ] 返回 true。
-z  检测字符串长度是否为0,为0返回 true。  [ -z $a ] 返回 false。
-n  检测字符串长度是否不为 0,不为 0 返回 true。 [ -n "$a" ] 返回 true。
$   检测字符串是否为空,不为空返回 true。   [ $a ] 返回 true。
文件测试运算符
文件测试运算符用于检测 Unix 文件的各种属性。

属性检测描述如下:

操作符 说明  举例
-b file 检测文件是否是块设备文件,如果是,则返回 true。  [ -b $file ] 返回 false。
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。    [ -f $file ] 返回 true。
-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。  [ -g $file ] 返回 false。
-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。  [ -k $file ] 返回 false。
-p file 检测文件是否是有名管道,如果是,则返回 true。   [ -p $file ] 返回 false。
-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。  [ -u $file ] 返回 false。
-r file 检测文件是否可读,如果是,则返回 true。  [ -r $file ] 返回 true。
-w file 检测文件是否可写,如果是,则返回 true。  [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。    [ -e $file ] 返回 true。

2.6 shell 打印输出

2.6.1 echo命令

Shell 的 echo 指令是用于字符串的输出。命令格式:

echo string

如:

# 普通的echo输出
echo " it is a echo test "

# 显示变量
echo " ${value}"  

# 换行,需要加-e,-e是开启转义
echo  -e " this a test turn line \n turn successful " 

# 显示不换行
echo -e "OK! \c" # -e 开启转义 \c 不换行
echo "It is a test"

# 显示结果定向至文件
echo "It is a test" > myfile

# 原样输出字符串,不进行转义或取变量(用单引号)
echo '$name\"'

2.6.2 shell printf命令

printf 命令模仿 C 程序库(library)里的 printf() 程序。printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。

printf  format-string  [arguments...]

format-string: 为格式控制字符串
arguments: 为参数列表。
# 最简单的打印
$ printf "Hello, Shell\n"
Hello, Shell
# 复杂的printf
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg  
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876

结果:
姓名     性别   体重kg
郭靖     男      66.12
杨过     男      48.65
郭芙     女      47.99

%s %c %d %f 都是格式替代符,%s 输出一个字符串,%d 整型输出,%c 输出一个字符,%f 输出实数,以小数形式输出。
%-10s 指一个宽度为 10 个字符(- 表示左对齐,没有则表示右对齐),任何字符都会被显示在 10 个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
%-4.2f 指格式化为小数,其中 .2 指保留2位小数。
# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
printf %s abc def
结果:abcdef
# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"
结果: and 0
shell脚本_第4张图片

2.7 shell test命令

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。

-eq 等于则为真
-ne 不等于则为真
-gt 大于则为真
-ge 大于等于则为真
-lt 小于则为真
-le 小于等于则为真
num1=100
num2=100
if test $[num1] -eq $[num2]
then
    echo '两个数相等!'
else
    echo '两个数不相等!'
fi

shell中[]执行基本的算数运算,如

result=$[a=b]
# 等号两边,都不能有空格
shell脚本_第5张图片
文件测试
参数  说明
-e 文件名  如果文件存在则为真
-r 文件名  如果文件存在且可读则为真
-w 文件名  如果文件存在且可写则为真
-x 文件名  如果文件存在且可执行则为真
-s 文件名  如果文件存在且至少有一个字符则为真
-d 文件名  如果文件存在且为目录则为真
-f 文件名  如果文件存在且为普通文件则为真
-c 文件名  如果文件存在且为字符型特殊文件则为真
-b 文件名  如果文件存在且为块特殊文件则为真

另外,Shell 还提供了与( -a )、或( -o )、非( ! )三个逻辑操作符用于将测试条件连接起来,其优先级为: ! 最高, -a 次之, -o 最低

2.8 shell 流程控制

** sh 的流程控制不可为空,如果没有就不写else,如else{}是错的。**

2.8.1 if else

if-else的书写格式

if condition
then
    command1 
    command2
    ...
    commandN
else
    command 
fi

# 如果想写成一行,(适用于终端命令提示符)
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi

if else - if else书写格式

if condition1
then
    command1
elif condition2 
then 
    command2
else
    commandN
fi

# 举例:
a=10
b=20
if [ $a == $b ]
then
   echo "a 等于 b"
elif [ $a -gt $b ]
then
   echo "a 大于 b"
elif [ $a -lt $b ]
then
   echo "a 小于 b"
else
   echo "没有符合的条件"
fi

2.8.2 if else 和test命令结合使用

num1=$[2*3]
num2=$[1+5]
if test $[num1] -eq $[num2]
then
    echo '两个数字相等!'
else
    echo '两个数字不相等!'
fi

2.8.2 for 循环

for循环一般格式为:

for var in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done

#写成一行:
for var in item1 item2 ... itemN; do command1; command2… done;

当变量值在列表里,for 循环即执行一次所有命令,使用变量名获取列表中的当前取值。
命令可为任何有效的 shell 命令和语句。
in 列表可以包含替换、字符串和文件名。
in列表是可选的,如果不用它,for循环使用命令行的位置参数。

for loop in 1 2 3 4 5
do
    echo "The value is: $loop"
done
shell 中的 for 循环不仅可以用文章所述的方法。

对于习惯其他语言 for 循环的朋友来说可能有点别扭。

for((assignment;condition:next));do
    command_1;
    command_2;
    commond_..;
done;
如上所示,这里的 for 循环与 C 中的相似,但并不完全相同。

通常情况下 shell 变量调用需要加 $,但是 for 的 (()) 中不需要,下面来看一个例子:

#!/bin/bash
for((i=1;i<=5;i++));do
    echo "这是第 $i 次调用";
done;
执行结果:

这是第1次调用
这是第2次调用
这是第3次调用
这是第4次调用
这是第5次调用
与 C 中相似,赋值和下一步执行可以放到代码之前循环语句之中执行,
这里要注意一点:如果要在循环体中进行 for 中的 next 操作,记得变量要加 $,不然程序会变成死循环。

2.8.3 while 语句

while condition
do
    command
done
#!/bin/bash
int=1
while(( $int<=5 ))
do
    echo $int
    let "int++"
done

2.8.4 case ... esac

case ... esac 为多选择语句,与其他语言中的 switch ... case 语句类似,是一种多分枝选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记

case 值 in
模式1)  #每一模式必须以右括号结束
    command1
    command2
    ...
    commandN
    ;;
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac

可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。

2.9 shell 函数

linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。
所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
函数与命令的执行结果可以作为条件语句使用。要注意的是,和 C 语言不同,shell 语言中 0 代表 true,0 以外的值代表 false。

[ function ] funname [()]

{

    action;

    [return int;]

}

参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255)

funWithReturn(){
    echo "这个函数会对输入的两个数字进行相加运算..."
    echo "输入第一个数字: "
    read aNum
    echo "输入第二个数字: "
    read anotherNum
    echo "两个数字分别为 $aNum 和 $anotherNum !"
    return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"

PS:
$?:函数返回值在调用该函数后通过 $? 来获得
$? 仅对其上一条指令负责,一旦函数返回后其返回值没有立即保存入参数,那么其返回值将不再能通过 $? 获得。

2.9.2 函数参数

在Shell中,调用函数时可以向其传递参数。在函数体内部,通过$n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...

funWithParam(){
    echo "第一个参数为 $1 !"
    echo "第二个参数为 $2 !"
    echo "第十个参数为 $10 !"
    echo "第十个参数为 ${10} !"
    echo "第十一个参数为 ${11} !"
    echo "参数总数有 $# 个!"
    echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73

注意,$10不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。

另外,还有几个特殊字符用来处理参数:

参数处理    说明
$#  传递到脚本或函数的参数个数
$*  以一个单字符串显示所有向脚本传递的参数
$$  脚本运行的当前进程ID号
$!  后台运行的最后一个进程的ID号
$@  与$*相同,但是使用时加引号,并在引号中返回每个参数。
$-  显示Shell使用的当前选项,与set命令功能相同。
$?  显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

2.10 Shell 输入/输出重定向

命令  说明
command > file  将输出重定向到 file。
command < file  将输入重定向到 file。
command >> file 将输出以追加的方式重定向到 file。如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾,
n > file    将文件描述符为 n 的文件重定向到 file。
n >> file   将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m  将输出文件 m 和 n 合并。
n <& m  将输入文件 m 和 n 合并。
<< tag  将开始标记 tag 和结束标记 tag 之间的内容作为输入。

2.11 shell 文件包含

. filename   # 注意点号(.)和文件名中间有一空格
或
source filename

如:
. ./test1.sh
source ./test1.sh

2.12 正则表达式

正则表达式:用来在文件中匹配符合条件的字符串,正则是包含匹配。grep、awk、sed等
通配符:用来匹配符合条件的文件名,通配符是完全匹配。ls、find、cp这些命令不支持正则表达式,所以只能使用shell自己的通配符来进行匹配了

shell脚本_第6张图片

shell脚本_第7张图片

shell脚本_第8张图片

shell脚本_第9张图片

shell脚本_第10张图片

2.12.1 awk 命令

awk命令是一种处理文本文件的语言,是一个强大的文本分析工具,能够识别非制表符的空格。
命令格式:
awk '条件1{动作1} 条件2{动作2}…' 文件名
条件:
一般使用关系表达式作为条件
动作:
格式化输出;
流程控制语句

awk [选项参数] 'script' var=value file(s)
或
awk [选项参数] -f scriptfile var=value file(s)

参数说明:
-F fs or --field-separator fs
指定输入文件折分隔符,fs是一个字符串或者是一个正则表达式,如-F:。
-v var=value or --asign var=value
赋值一个用户定义变量。
-f scripfile or --file scriptfile
从脚本文件中读取awk命令。
-mf nnn and -mr nnn
对nnn值设置内在限制,-mf选项限制分配给nnn的最大块数目;-mr选项限制记录的最大数目。这两个功能是Bell实验室版awk的扩展功能,在标准awk中不适用。
-W compact or --compat, -W traditional or --traditional
在兼容模式下运行awk。所以gawk的行为和标准的awk完全一样,所有的awk扩展都被忽略。
-W copyleft or --copyleft, -W copyright or --copyright
打印简短的版权信息。
-W help or --help, -W usage or --usage
打印全部awk选项和每个选项的简短说明。
-W lint or --lint
打印不能向传统unix平台移植的结构的警告。
-W lint-old or --lint-old
打印关于不能向传统unix平台移植的结构的警告。
-W posix
打开兼容模式。但有以下限制,不识别:/x、函数关键字、func、换码序列以及当fs是一个空格时,将新行作为一个域分隔符;操作符**和**=不能代替^和^=;fflush无效。
-W re-interval or --re-inerval
允许间隔正则表达式的使用,参考(grep中的Posix字符类),如括号表达式[[:alpha:]]。
-W source program-text or --source program-text
使用program-text作为源代码,可与-f命令混用。
-W version or --version
打印bug报告信息的版本

awk举例:
有文件log.txt

2 this is a test
3 Are you like awk
This's a test
10 There are orange,apple,mongo

用法1:awk '{[pattern] action}' {filenames} # 行匹配语句 awk ' ' 只能用单引号

# 每行按空格或TAB分割,输出文本中的1、4项
 $ awk '{print $1,$4}' log.txt
 2 a
 3 like
 This's
 10 orange,apple,mongo

 # 格式化输出
 $ awk '{printf "%-8s %-10s\n",$1,$4}' log.txt
 ---------------------------------------------
 2        a
 3        like
 This's
 10       orange,apple,mongo

用法2:awk -F #-F相当于内置变量FS, 指定分割字符, (相当于条件)

# 使用","分割
 $  awk -F, '{print $1,$2}'   log.txt
 ---------------------------------------------
 2 this is a test
 3 Are you like awk
 This's a test
 10 There are orange apple

 # 或者使用内建变量
 $ awk 'BEGIN{FS=","} {print $1,$2}'     log.txt
 ---------------------------------------------
 2 this is a test
 3 Are you like awk
 This's a test
 10 There are orange apple

 # 使用多个分隔符.先使用空格分割,然后对分割结果再使用","分割
 $ awk -F '[ ,]'  '{print $1,$2,$5}'   log.txt
 ---------------------------------------------
 2 this test
 3 Are awk
 This's a
 10 There apple

用法3: awk -v # 设置变量

$ awk -va=1 '{print $1,$1+a}' log.txt
 ---------------------------------------------
 2 3
 3 4
 This's 1
 10 11

 $ awk -va=1 -vb=s '{print $1,$1+a,$1b}' log.txt
 ---------------------------------------------
 2 3 2s
 3 4 3s
 This's 1 This'ss
 10 11 10s

三、shell实例

N: shell 杂项知识

  1. read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量

  2. let:let 命令是 BASH 中用于计算的工具,用于执行一个或多个表达式,变量计算中不需要加上 $来表示变量。如果表达式中包含了空格或其他特殊字符,则必须引起来。
    let arg [arg ...].
    let no++

  3. echo 转义符
    -n 不输出换行符
    -e 打开反斜杠ESC转义
    -E 取消反斜杠ESC转义 (默认)


    shell脚本_第11张图片
  4. shell “” '' ` `的作用: `` 有直接执行命令的作用

    shell脚本_第12张图片

#!/bin/bash
name="abc"
name1='edf'
echo "\start + "$name" + '$name' + "$name1" + '$name1' + end\ !"
结果:
\start + abc + 'abc' + edf + 'edf' + end\ !

commandLine1=`grep "test cases ran." ./${logFileName} | awk -F " " '{print $2}'`

  1. $+variable: 执行变量中的指令
    shell脚本_第13张图片
  2. ${name##*/}guding 舍掉路径,只取文件名
    ${#[@]} 数组元素的范围
    SourceFiles=() ,后面可以使用
  3. 通配符


    shell脚本_第14张图片

你可能感兴趣的:(shell脚本)