学习整理之菜鸟驿站:
一、什么是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变量
- 命名变量:
your_name="runoob.com"
书写注意点:变量名和等号之间不能有空格。 - 使用变量:
echo ${your_name}
注意点说明:1、使用已定义过的变量,只要在变量名前面加美元符号即可;
2、大括号是可选的,但是最好加一下,方便阅读;
3、**如果想第二次赋值,不要加$(只有使用时加),直接赋值就好,your_name="tom"
4、使用命令readonly可以使变量只读,不能被改变,如readonly your_name
- 删除变量:
unset your_name
- 变量类型:
1、局部变量:局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量
2、环境变量: 所有的程序,包括shell启动的程序,都能访问环境变量
3、shell变量:shell变量是由shell程序设置的特殊变量
2.2 shell 字符串
字符串和变量类似,字符串可以用单引号,也可以用双引号,也可以不用引号。
- 单引号:
str='this is a string'
限制:
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。 - 双引号:
优点:双引号里可以有变量;双引号里可以出现转义字符
your_name='runoob'
str="Hello, I know you are \"$your_name\"! \n"
echo -e $str
输出:Hello, I know you are "runoob"!
- 拼接字符串:
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:单引号内不能使用变量。
- 获取字符串长度:
echo ${#string}
- 提取子字符串:echo ${string:1:4}
PS:第一个字符的索引值为 0 - 查找子字符串:下面使用的是反引号 `,不是单引号 '
查找字符 i 或 o 的位置(哪个字母先出现就计算哪个):
string="runoob is a great site"
echo `expr index "$string" io` # 输出 4
2.3 shell 传递参数
我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……
特殊字符来处理参数:
| $# | 传递到脚本的参数个数 |
| $* | 以一个单字符串显示所有向脚本传递的参数。
如"$*"用「"」括起来的情况、以"$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 键下边。
关系运算符
关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
下表列出了常用的关系运算符,假定变量 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。
字符串运算符
下表列出了常用的字符串运算符,假定变量 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
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]
# 等号两边,都不能有空格
文件测试
参数 说明
-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自己的通配符来进行匹配了
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 杂项知识
read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量
let:let 命令是 BASH 中用于计算的工具,用于执行一个或多个表达式,变量计算中不需要加上
$
来表示变量。如果表达式中包含了空格或其他特殊字符,则必须引起来。
let arg [arg ...]
.
如let no++
-
echo 转义符
-n 不输出换行符
-e 打开反斜杠ESC转义
-E 取消反斜杠ESC转义 (默认)
-
shell
“” '' ` `
的作用: `` 有直接执行命令的作用
#!/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}'`
-
$+variable:
执行变量中的指令
-
${name##*/}
guding 舍掉路径,只取文件名
${#[@]}
数组元素的范围
SourceFiles=() ,后面可以使用 -
通配符