Shell教程2:常用命令与流程控制

S h e l l Shell Shell 脚本语言教程 2 2 2

Copyright: Jingmin Wei, Pattern Recognition and Intelligent System, School of Artifical Intelligence and Automation, Huazhong University of Science and Technology

这一章节的黑体标粗都是非常重要的细节!


文章目录

      • S h e l l Shell Shell 脚本语言教程 2 2 2
        • let, expr 和 (()) 命令
        • echo & read 命令
        • printf 命令
          • printf的转义序列
        • test 和 [] 命令
          • 数值测试
          • 字符串测试
          • 文件测试
          • [] 和 test
        • [[ ]] 命令
        • If then - elif then - else - fi
        • for ... in ... - do - done 和 for (( ; ; ))
          • 数组遍历
          • 字符遍历
        • while - do - done
        • 无限循环
        • until - do - done
        • case ... in - 1) ;; 2) ;; 3) ;; - esac
        • 跳出循环
          • break
          • continue


Linux 专栏链接

本教程为Shell脚本语言学习笔记,且持续更新中。不商用,如需转载请联系本人。

其中的大部分截图都是本人在 Shell 小黑窗上自己尝试的结果,请参考者记得根据自己 Linux 系统的实际情况来进行修改。

let, expr 和 (()) 命令

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 & read 命令

用于字符串的输出,可以实现更复杂的格式控制

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`

Shell教程2:常用命令与流程控制_第1张图片

Shell教程2:常用命令与流程控制_第2张图片

printf 命令

printf 命令模仿 C 程序库里的 printf() 程序标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。

printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。

printf 命令的语法:

printf  format-string  [arguments...]

参数说明:

  • 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" 

Shell教程2:常用命令与流程控制_第3张图片

printf的转义序列
序列 说明
\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 和 [] 命令用于检查某个条件是否成立,它们可以进行数值、字符和文件三个方面的测试。

前一章在逻辑运算和布尔运算中,我们已经学习了 [] 的使用,所以这里我们先看 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 ,两者基本是一样的,我们之前在逻辑运算符中,说了 [] 的使用。接下来我们说下区别。

# 二者等价
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" ] # 在[]结构中"<"需要被转义.

字符表达式的比较,文件表达式的测试,逻辑表达式的测试,使用 [[ ]] 最好。

If then - elif then - else - fi

注意,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 … in … - do - done 和 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 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

Shell教程2:常用命令与流程控制_第4张图片

while - do - 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 - do - done

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) ;; 2) ;; 3) ;; - esac
casein
模式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

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

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"永远不会被执行。

你可能感兴趣的:(Linux,linux,ubuntu,操作系统,shell)