Copyright: Jingmin Wei, Pattern Recognition and Intelligent System, School of Artifical Intelligence and Automation, Huazhong University of Science and Technology
这一章节的黑体标粗都是非常重要的细节!
Linux 专栏链接
本教程为Shell脚本语言学习笔记,且持续更新中。不商用,如需转载请联系本人。
其中的大部分截图都是本人在 Shell 小黑窗上自己尝试的结果,请参考者记得根据自己 Linux 系统的实际情况来进行修改。
let 与 (()) 在 shell 编程中是可以互换的,它们让我们可以像 C 语言那样写程序对于变量运算赋值。在循环语句中控制变量变化非常有用。
使用 let 时,运算符两边不能有空格。let可以解析 " " 中间的表达式
使用 (( )) 时,运算符两边需要有空格。
(( )) 算数表达式的值在 if 和 while 判断时不用在前面加“$” ,在echo输出值时需要加"$" 。
echo $(( c = a + b + 1 ))
let 和 (()) 都只能对整数进行运算赋值。
`expr`需要用到 $ 使用变量,let 和 (( )) 平时不需要,echo输出只要在最前面加。
使用 let 或 (()) 赋值时,也可以使用 expr + 反引号代替(如下,二者等价):
let llines=llines+fileLines
llines=`expr $llines + $fileLines`
但是和 expr 不同,let 和 (( )) 命令的 * 不需要加上 \* 。
a=1;b=2
let c=a+b # 使用let,不能有空格
echo $c
let a=11*11
(( c = a + b )) # 使用(()),必须有空格
echo $c
x=12
let "x<10"
echo $?
使用逻辑和关系操作符,(!,<=,>=,<,>,++,~=),的时候,shell 会返回一个代码变量,$? 会反映结果是真还是假。必须使用双引号来防止 shell 将大于和小于运算符当作 I/O 重定向。
数字的比较一般使用 (( )) ,而不是 [[ ]] 。 有些比较运算符在 [[ ]] 中无效。
(( )) 支持 ; 隔开运算,因此也被用于传统的 for 循环。
用于字符串的输出,可以实现更复杂的格式控制
echo string
普通字符,两种显示方式
(联想文件的绝对路径,如果不存在带空格的文件夹,也可以选择是否带引号)
echo It is a test
echo "It is a test"
转义字符
echo "\"It is a test\""
read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量
#!/bin/bash
read name
echo "$name It is a test"
显示换行
echo -e "OK!\n" # -e开启转义,\n换行
echo "It is a test"
显示不换行
echo -e "OK!\c" # -e开启转义,\c不换行
echo "It is a test"
结果定向到文件
echo "It is a test" > testfile.txt
用单引号即可原样输出字符串,不进行转义或取变量
echo '$name\"'
显示命令执行结果
echo `date`
测试代码:
#!/bin/bash
echo It is a test
echo "It is a test"
echo "\"It is a test\""
# 输入
read name
echo "$name It is a test"
# 转义
echo -e "OK!\n" # -e开启转义,\n换行
echo "It is a test"
echo -e "OK!\c" # -e开启转义,\c不换行
echo "It is a test"
# 重定向至文件
echo "It is a test" > testfile.txt
# 单引号
echo '$name\"'
echo `date`
printf 命令模仿 C 程序库里的 printf() 程序标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。
printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。
printf 命令的语法:
printf format-string [arguments...]
参数说明:
printf 不会自动换行,但是也不需要像echo一样用-e开启转义字符。
echo "Hello world!"
printf "Hello world!\n"
格式化输出
#!/bin/bash
printf "%-10s %-8s %-4s\n" name gender weight\(kg\)
printf "%-10s %-8s %-4.2f\n" Weijingmin Male 53.5
printf "%-10s %-8s %-4.2f\n" Zhaoxuan Male 50.8267
printf "%-10s %-8s %-4.2f\n" Chenqin Male 55.143
%s %c %d %f都是格式替代符
%-10s 指一个宽度为 10 个字符(-表示左对齐,没有则表示右对齐),任何字符都会被显示在 10 个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
%-4.2f 指格式化为小数,其中 .2 指保留 2 位小数。
#!/bin/bash
# format-string为双引号
printf "%d %s\n" 1 "abc"
# 单引号与双引号效果一样
printf '%d %s\n' 1 "abc"
# 没有引号也可以输出
printf %s abcdef
# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
printf %s abc def
printf "%s\n" abc def
printf "%s %s %s\n" a b c d e f g h i j
# 如果没有 arguments,那么默认情况下 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"
序列 | 说明 |
---|---|
\a | 警告字符,通常为 ASCII 的 BEL 字符 |
\b | 后退 |
\c | 抑制(不显示)输出结果中任何结尾的换行字符(只在 %b 格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略 |
\f | 换页(formfeed) |
\n | 换行 |
\r | 回车(Carriage return) |
\t | 水平制表符 |
\v | 垂直制表符 |
\ | 一个字面上的反斜杠字符 |
\ddd | 表示 1 到 3 位数八进制值的字符。仅在格式字符串中有效 |
\0ddd | 表示 1 到 3 位的八进制值字符 |
$ printf "a string, no processing:<%s>\n" "A\nB"
a string, no processing:<A\nB>
$ printf "a string, no processing:<%b>\n" "A\nB"
a string, no processing:<A
B>
$ printf "Teacher Wei \a"
Teacher Wei $ # 不换行
test 和 [] 命令用于检查某个条件是否成立,它们可以进行数值、字符和文件三个方面的测试。
前一章在逻辑运算和布尔运算中,我们已经学习了 [] 的使用,所以这里我们先看 test 的使用。
参数 | 说明 |
---|---|
-eq | 等于则为真 |
-ne | 不等于则为真 |
-gt | 大于则为真 |
-ge | 大于等于则为真 |
-lt | 小于则为真 |
-le | 小于等于则为真 |
新建test_test.sh
num1=100
num2=100
if test $[num1] -eq $[num2]
then
echo '两个数相等!'
else
echo '两个数不相等!'
fi
参数 | 说明 |
---|---|
= 或者 == | 等于则为真 |
!= | 不相等则为真 |
-z 字符串 | 字符串长度为零则为真 |
-n 字符串 | 字符串长度不为零则为真 |
test_test.sh 后加上如下代码
str1="Weijingmin"
str2="Weijingmin"
if test str1=str2
then
echo '两个字符串相等!'
else
echo '两个字符串不相等!'
fi
参数 | 说明 |
---|---|
-e 文件名 | 如果文件存在则为真 |
-r 文件名 | 如果文件存在且可读则为真 |
-w 文件名 | 如果文件存在且可写则为真 |
-x 文件名 | 如果文件存在且可执行则为真 |
-s 文件名 | 如果文件存在且至少有一个字符则为真 |
-d 文件名 | 如果文件存在且为目录则为真 |
-f 文件名 | 如果文件存在且为普通文件则为真 |
-c 文件名 | 如果文件存在且为字符型特殊文件则为真 |
-b 文件名 | 如果文件存在且为块特殊文件则为真 |
实例演示:
cd /bin
if test -e ./bash
then
echo '文件已存在!'
else
echo '文件不存在!'
fi
另外,Shell还提供了与( -a )、或( -o )、非( ! )三个逻辑操作符用于将测试条件连接起来,其优先级为:"!“最高,”-a"次之,"-o"最低。例如:
cd /bin
if test -e ./notFile -o -e ./bash
then
echo '有一个文件存在!'
else
echo '两个文件都不存在'
fi
首先说明,使用 [ ] 和 [[ ]] 的时候不要吝啬空格,每一项两边都要有空格。
[] 和 test ,两者基本是一样的,我们之前在逻辑运算符中,说了 [] 的使用。接下来我们说下区别。
# 二者等价
test expr1
[ expr1 ]
在命令行里效果相同。他们的三个基本作用是判断文件、判断字符串、判断整数。支持使用与或非将表达式连接起来。要注意的有:
test 中可用的比较运算符只有 =, == 和 != , 三者都是用于字符串比较的,不可用于整数比较,整数比较可以使用-eq, -gt这种形式。
[] 中可以用 == 和 != 来比较整数和小数。
无论是字符串比较还是整数比较都千万不要使用大于号小于号。 当然,如果你实在想用也是可以的,对于字符串比较可以使用尖括号的转义形式,如果比较 “ab” 和 “bc” :[ ab < bc ],结果为真,也就是返回状态为0。
test 和 [] 中的逻辑运算与或非只能使用 -a、-o、!
#/bin/bash
a="ab"
if [ $a = "ab" ]
then
echo True
else
echo False
fi
var1=1;var2=2
if test $var1 -eq 2 -a $var2 -eq 1
then
echo Equal
else
echo No equal
fi
var1=1.0;var2=2.0
if [ $var1 == 1.0 -a $var2 == 2.0 ]
then
echo Equal
else
echo No equal
fi
[[ ]] 类似于 [ ] ,但它有很多强大的功能。
它首先支持字符串的模式匹配(使用 =~ 操作符时甚至支持 shell 的正则表达式)。
其次,[[ ]] 中逻辑组合可以不使用 test 的 -a,-o ,而使用 &&,|| 这样更亲切的形式。
[ ] 需要转义 \> \< 符号,因为它是命令,不加转义的话,就变成文件重定向运算了。但是 [[ ]] 作为高级的关键字,不会作命令行扩展,也就不需要转义。
[[ ]] 可作算术的扩展,但是 [ ] 不行。
字符串比较时可以把右边的作为一个模式(这是右边的字符串不加双引号的情况下。如果右边的字符串加了双引号,则认为是一个文本字符串。),而不仅仅是一个字符串,比如[[ hello == hell? ]],结果为真。
常见的正则表达式可以去看 python, Java 相关。表达式
[ ] | [[ ]] | |
---|---|---|
数字测试 | -eq -ne -lt -le -gt -ge | 同 [ ] |
文件测试 | -r -l -w -x -f -d -s -nt -ot | 同 [ ] |
字符测试 | = != -n -z ==(同=) \< \> 不可以用 >= <= | 同 [ ] , 但> < 不需要转义 |
逻辑测试 | -a -o ! (不做逻辑短路) | && || ! (遵守逻辑短路) |
数学运算 | 不可以使用 | + - * / % |
表达式# 模式匹配
[[ $a == z* ]] # 如果$a以"z"开头(模式匹配)那么将为true
[[$a == "z*" ]] # 如果$a等于z*(字符匹配),那么结果为true
[ $a == "z*" ]
[ $a == z* ] # 效果一样,如果$a等于z*(字符匹配),那么结果为true
if [ "$a" != "$b" ] # !=不等于, 这个操作符将在[[]]结构中使用模式匹配.
# 小于,在ASCII字母顺序下.如:
if [[ "$a" < "$b" ]]
if [ "$a" \< "$b" ] # 在[]结构中"<"需要被转义.
字符表达式的比较,文件表达式的测试,逻辑表达式的测试,使用 [[ ]] 最好。
注意,sh的流程控制不可为空,即如果else分支没有语句执行,就不要写这个else。
if 语句语法格式:
if condition
then
command1
command2
...
commandN
fi
写成一行(适用于终端命令提示符):
ps -ef | grep 用来查看服务是否启动
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
末尾的fi就是if倒过来拼写,后面还会遇到类似的。
if else 语法格式:
if condition
then
command1
command2
...
commandN
else
command
fi
if else-if else 语法格式:
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
if else语句经常与test命令结合使用,如下所示:
#!/bin/bash
num1=$[2*3]
num2=$[1+5]
if test $[num1] -eq $[num2]
then
echo '两数相等'
else
echo '两数不等'
fi
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 i in 1 2 3 4 5
do
echo "The value is: $i"
done
array=(1 2 3 4 5)
# 使用 (( )) 的标准循环
for (( i=0; i<${#array[@]}; i++ )); do
echo "The value is: ${array[i]}"
done
# 不带下标循环,实质上也是把数组的所有元素列出来再挨个取值
for i in ${array[@]}; do
echo "The value is: $i"
done
# 带下标循环
for i in "${!array[@]}"; do
printf "%d\t" "${array[i]}"
done
for chr in 'This is a string'
do
echo $chr
done
while循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件。其格式为:
while condition
do
command
done
以下是一个基本的while循环,测试条件是:如果int小于等于5,那么条件返回真。int从0开始,每次循环处理时,int加1。运行上述脚本,返回数字1到5,然后终止。
#!/bin/bash
int=1
while(( $int <= 5 ))
do
echo $int
let int++
done
while循环可用于读取键盘信息。下面的例子中,输入信息被设置为变量FILM,按 Ctrl-D 结束循环。
echo -n '输入你最喜欢的电影名: '
while read FILM
do
echo "是的!$FILM 是一部好电影"
done
格式:
while :
do
command
done
while true
do
command
done
for (( ; ; ))
until 循环执行一系列命令直至条件为真时停止。
until 循环当条件不成立时才进入循环,条件成立则退出,与 while 循环在处理方式上刚好相反。
一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。
until 语法格式:
until condition
do
command
done
条件可为任意测试条件,测试发生在循环末尾,因此循环至少执行一次。
计算1-100的和
#!/bin/bash
tmp=0
until (( tmp >= 100 ))
do
let tmp++
let result+=tmp
done
echo "result=$result"[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-15rANjCs-1646710889639)(/home/weijingmin/图片/2022-01-29 19-32-52 的屏幕截图.png)]
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
case工作方式如上所示。取值后面必须为单词 in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;; 。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。
下面的脚本提示输入 1 到 4 ,与每一种模式进行匹配:
#!/bin/bash
echo 'please input a number:'
read num
case $num in
1) echo 'you input 1'
;;
2) echo 'you input 2'
;;
3) echo 'you input 3'
;;
*) echo 'the number you input is not range from 1 to 3'
;;
esac
输入不同的内容,会有不同的结果。
在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell 使用两个命令来实现该功能:break 和 continue 。
break命令允许跳出所有循环(终止执行后面的所有循环)。
下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令。
#!/bin/bash
while :
do
read num
case $num in
1|2|3|4|5)
echo "the number ${num} is range from 1 to 5"
;;
*)
echo "game over"
break
;;
esac
done
continue命令不会跳出所有循环,仅仅跳出当前循环轮。
#!/bin/bash
while :
do
echo 'please input a number: '
read num
case $num in
1|2|3|4|5)
echo "the number ${num} is range from 1 to 5"
;;
*)
continue
echo "game over"
;;
esac
done
当输入大于5的数字时,该例中的循环不会结束,语句 echo "game over"永远不会被执行。