shell是一种脚本语言,在日常工作中用到较多,无论是管理airflow调度,还是管理集群,都需要进行shell脚本的编写。今天抽时间整理了一下shell的相关使用。
1.第一个脚本程序
1 | #!/bin/bash |
2.变量
2.1 定义变量
1 | country="China" |
2.2 使用变量
1 | country="China" |
2.3 重定义变量
变量重新赋值就可以了:1
2country="China"
country="ribenguizi"
2.4 删除变量
删除变量: 使用unset命令可以删除变量,但是不能删除只读的变量。用法:1
unset variable_name
2.5 变量类型
1) 局部变量
局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
2) 环境变量
所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
3) shell变量
shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行
4)只读变量1
2
3
4readonly country="China"
#或
country="China"
readonly country
特殊变量:
变量 | 含义 |
---|---|
$0 | 当前脚本的文件名 |
$n | 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。 |
$# | 传递给脚本或函数的参数个数。 |
$* | 传递给脚本或函数的所有参数。 |
$@ | 传递给脚本或函数的所有参数。被双引号(“ “)包含时,与 $* 稍有不同,下面将会讲到。 |
$? | 上个命令的退出状态,或函数的返回值。 |
$$ | 当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。 |
3.shell替换
3.1转义字符
如果表达式中包含特殊字符,Shell 将会进行替换。例如,在双引号中使用变量就是一种替换,转义字符也是一种替换。
举例:1
2
3#!/bin/bash
a=10
echo -e "Value of a is $a \n"
运行结果:1
Value of a is 10
这里 -e 表示对转义字符进行替换。如果不使用 -e 选项,将会原样输出:1
Value of a is 10\n
下面的转义字符都可以用在echo中:
转义字符 | 含义 |
---|---|
\ | 反斜杠 |
\a | 警报,响铃 |
\b | 退格(删除键) |
\f | 换页(FF),将当前位置移到下页开头 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符(tab键) |
\v | 垂直制表符 |
3.2 命令替换
它的意思就是说我们把一个命令的输出赋值给一个变量,方法为把命令用反引号(在Esc下方)引起来。比如:1
2directory=`pwd`
echo $directory
3.3 变量替换
可以根据变量的状态(是否为空、是否定义等)来改变它的值。
形式 | 说明 |
---|---|
${var} | 变量本来的值 |
${var:-word} | 如果变量 var 为空或已被删除(unset),那么返回 word,但不改变 var 的值。 |
${var:=word} | 如果变量 var 为空或已被删除(unset),那么返回 word,并将 var 的值设置为 word。 |
${var:?message} | 如果变量 var 为空或已被删除(unset),那么将消息 message 送到标准错误输出,可以用来检测变量 var 是否可以被正常赋值。若此替换出现在Shell脚本中,那么脚本将停止运行。 |
${var:+word} | 如果变量 var 被定义,那么返回 word,但不改变 var 的值。 |
4.shell运算符
算数运算符
原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr。 下面使用expr进行;expr是一款表达式计算工具,使用它可以完成表达式的求值操作。
运算符 | 说明 | 举例 |
---|---|---|
+ | 加法 | expr $a + $b 结果为 30。 |
- | 减法 | expr $a - $b 结果为 10。 |
* | 乘法 | expr $a \* $b 结果为 200。 |
/ | 除法 | expr $b / $a 结果为 2。 |
% | 取余 | expr $b % $a 结果为 0。 |
= | 赋值 | a=$b 将把变量 b 的值赋给 a。 |
== | 相等。用于比较两个数字,相同则返回 true。 | [ $a == $b ] 返回 false。 |
!= | 不相等。用于比较两个数字,不相同则返回 true。 | [ $a != $b ] 返回 true。 |
比如:
1 | a=10 |
注意:
1.在expr中的乖号为:*
2.在 expr中的 表达式与运算符之间要有空格,否则错误;
3.在[ $a == $b ]与[ $a != $b ]中,要需要在方括号与变量以及变量与运算符之间也需要有括号, 否则为错误的。
关系运算符
只支持数字,不支持字符串,除非字符串的值是数字。常见的有:
运算符 | 说明 | 举例 |
---|---|---|
-eq | 检测两个数是否相等,相等返回 true。 | [ $a -eq $b ] 返回 true。 |
-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。 |
注意:也别忘记了空格;
布尔运算符:
运算符 | 说明 | 举例 |
---|---|---|
! | 非运算,表达式为 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。 |
字符串运算符
运算符 | 说明 | 举例 |
---|---|---|
= | 检测两个字符串是否相等,相等返回 true。 | [ $a = $b ] 返回 false。 |
!= | 检测两个字符串是否相等,不相等返回 true。 | [ $a != $b ] 返回 true。 |
-z | 检测字符串长度是否为0,为0返回 true。 | [ -z $a ] 返回 false。 |
-n | 检测字符串长度是否为0,不为0返回 true。 | [ -z $a ] 返回 true。 |
str | 检测字符串是否为空,不为空返回 true。 | [ $a ] 返回 true。 |
文件测试运算符:
检测Unix文件的各种属性。
操作符 | 说明 | 举例 |
---|---|---|
-b file | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] 返回 false。 |
-c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | [ -b $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。 |
5.shell中的字符串
单引号的限制:
1.单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
2.单引号字串中不能出现单引号(对单引号使用转义符后也不行)。
双引号的优点:
1.双引号里可以有变量
2.双引号里可以出现转义字符
拼接字符串:1
2
3
4country="China"
echo "hello, $country"
#也可以
echo "hello, "$country" "
获取字符串长度:1
2string="abcd"
echo ${#string} #输出 4
提取子字符串:1
2string="alibaba is a great company"
echo ${string:1:4} #输出liba
查找子字符串:1
2string="alibaba is a great company"
echo `expr index "$string" is`
处理路经的字符串:
例如:当一个路径为 /home/xiaoming/1.txt时,如何怎么它的路径(不带文件) 和如何得到它的文件名??
得到文件名使用 bashname命令:1
2
3
4
5
6
7
8
9
10# 参数:
# -a,表示处理多个路径;
# -s, 用于去掉指定的文件的后缀名;
basename /home/yin/1.txt -> 1.txt
basename -a /home/yin/1.txt /home/zhai/2.sh ->
1.txt
2.sh basename -s .txt /home/yin/1.txt -> 1
basename /home/yin/1.txt .txt -> 1
得到路径名(不带文件名)使用 dirname命令:1
2
3
4
5
6
7
8参数:没有啥参数;
//例子:
dirname /usr/bin/ -> /usr
dirname dir1/str dir2/str ->
dir1
dir2
dirname stdio.h -> .
6.shell的数组
bash支持一维数组, 不支持多维数组, 它的下标从0开始编号. 用下标[n] 获取数组元素;
定义数组:
在shell中用括号表示数组,元素用空格分开。 如:1
array_name=(value0 value1 value2 value3)
也可以单独定义数组的各个分量,可以不使用连续的下标,而且下标的范围没有限制。如:1
2
3array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
读取数组:
读取某个下标的元素一般格式为:1
${array_name[index]}
读取数组的全部元素,用@或*1
2${array_name[*]}
${array_name[@]}
获取数组的信息:
取得数组元素的个数:1
2
3length=${#array_name[@]}
#或
length=${#array_name[*]}
获取数组的下标:1
2
3length=${!array_name[@]}
#或
length=${!array_name[*]}
取得数组单个元素的长度:1
lengthn=${#array_name[n]}
7.条件语句
if else语句
Shell 有三种 if … else 语句:
- if … fi 语句;
- if … else … fi 语句;
- if … elif … else … fi 语句。
例子:1
2
3
4
5
6
7
8a=10
b=20
if [ $a == $b ]
then
echo "a is equal to b"
else
echo "a is not equal to b"
fi
另外:if … else 语句也可以写成一行,以命令的方式来运行,像这样:1
if test $[2*3] -eq $[1+5]; then echo 'The two numbers are equal!'; fi;
其中,test 命令用于检查某个条件是否成立,与方括号([ ])类似。
case …… esac语句
case … esac 与其他语言中的 switch … case 语句类似,是一种多分枝选择结构。case语句格式如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17case 值 in
模式1)
command1
command2
command3
;;
模式2)
command1
command2
command3
;;
*)
command1
command2
command3
;;
esac
其中, 1. 取值后面必须为关键字 in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。;; 与其他语言中的 break 类似,意思是跳到整个 case 语句的最后。2. 如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。
8.循环语句
for 循环
一般格式为:
1 | for 变量 in 列表 |
注意:列表是一组值(数字、字符串等)组成的序列,每个值通过空格分隔。每循环一次,就将列表中的下一个值赋给变量。例如:
顺序输出当前列表的数字:1
2
3
4for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done
显示主目录下以 .bash 开头的文件:1
2
3
4
5#!/bin/bash
for FILE in $HOME/.bash*
do
echo $FILE
done
while循环
一般格式为:1
2
3
4while command
do
Statement(s) to be executed if command is true
done
例如:1
2
3
4
5
6COUNTER=0
while [ $COUNTER -lt 5 ]
do
COUNTER='expr $COUNTER+1'
echo $COUNTER
done
until 循环
until 循环执行一系列命令直至条件为 true 时停止。until 循环与 while 循环在处理方式上刚好相反。 常用格式为:1
2
3
4until command
do
Statement(s) to be executed until command is true
done
command 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。
类似地, 在循环中使用 break 与continue 跳出循环。 另外,break 命令后面还可以跟一个整数,表示跳出第几层循环。
函数
Shell函数必须先定义后使用,定义如下,1
2
3
4function_name () {
list of commands
[ return value ]
}
也可以加上function关键字:1
2
3
4function function_name () {
list of commands
[ return value ]
}
注意:
1.调用函数只需要给出函数名,不需要加括号。
2.函数返回值,可以显式增加return语句;如果不加,会将最后一条命令运行结果作为返回值。
3.Shell 函数返回值只能是整数,一般用来表示函数执行成功与否,0表示成功,其他值表示失败。
4.函数的参数可以通过 $n 得到.如:1
2
3
4
5
6
7
8
9funWithParam(){
echo "The value of the first parameter is $1 !"
echo "The value of the second parameter is $2 !"
echo "The value of the tenth parameter is ${10} !"
echo "The value of the eleventh parameter is ${11} !"
echo "The amount of the parameters is $# !" # 参数个数
echo "The string of the parameters is $* !" # 传递给函数的所有参数
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
5.像删除变量一样,删除函数也可以使用 unset 命令,不过要加上 .f 选项,如下所示:1
unset .f function_name
shell的文件包含
Shell 也可以包含外部脚本,将外部脚本的内容合并到当前脚本。使用:1
2
3. filename
#或
source filename
两种方式的效果相同,简单起见,一般使用点号(.),但是注意点号(.)和文件名中间有一空格。
被包含脚本不需要有执行权限。