《Linux – Linux基础》第5章 shell编程(二)

5.5 shell程序设计基础

5.5.1 Shell替换

如果表达式中包含特殊字符,Shell 将会进行替换。例如,在双引号中使用变量就是一种替换,转义字符也是一种替换。

举个例子:

#!/bin/bash  
a=10  
echo -e "Value of a is $a \n"  

运行结果:

Value of a is 10

这里 -e 表示对转义字符进行替换。如果不使用 -e 选项,将会原样输出:

Value of a is 10\n

下面的转义字符都可以用在 echo 中。

转义字符 含义
\ 反斜杠
\a 警报,响铃
\b 退格(删除键)
\f 换页(FF),将当前位置移到下页开头
\n 换行
\r 回车
\t 水平制表符(tab键)
\v 垂直制表符

可以使用 echo 命令的 -E 选项禁止转义,默认也是不转义的;使用 -n 选项可以禁止插入换行符。

1)命令替换
命令替换是指Shell可以先执行命令,将输出结果暂时保存,在适当的地方输出。
命令替换的语法:
command

注意是反引号,不是单引号,这个键位于 Esc 键下方。

下面的例子中,将命令执行结果保存在变量中:

#!/bin/bash  
DATE=`date`  
echo "Date is $DATE"  
USERS=`who | wc -l`  
echo "Logged in user are $USERS"  
UP=`date ; uptime`  
echo "Uptime is $UP"  

运行结果:

Date is Thu Jul  2 03:59:57 MST 2009
Logged in user are 1
Uptime is Thu Jul  2 03:59:57 MST 2009
03:59:57 up 20 days, 14:03,  1 user,  load avg: 0.13, 0.07, 0.15

2)变量替换

变量替换可以根据变量的状态(是否为空、是否定义等)来改变它的值 可以使用的变量替换形式。

形式 说明
${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 的值。

请看下面的例子:

#!/bin/bash

echo ${var:-"Variable is not set"}
echo "1 - Value of var is ${var}"

echo ${var:="Variable is not set"}
echo "2 - Value of var is ${var}"

unset var
echo ${var:+"This is default value"}
echo "3 - Value of var is $var"

var="Prefix"
echo ${var:+"This is default value"}
echo "4 - Value of var is $var"

echo ${var:?"Print this message"}
echo "5 - Value of var is ${var}"

运行结果:

Variable is not set  
1 - Value of var is  
Variable is not set  
2 - Value of var is 
Variable is not set  
3 - Value of var is  
This is default value  
4 - Value of var is Prefix  
Prefix  
5 - Value of var is Prefix  

5.5.2 Shell运算符

Bash 支持很多运算符,包括算数运算符、关系运算符、布尔运算符、字符串运算符和文件测试运算符。

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。
expr 是一款表达式计算工具,使用它能完成表达式的求值操作。

例如,两个数相加:

#!/bin/bash  
val=`expr 2 + 2`  
echo "Total value : $val"  

运行脚本输出:

Total value : 4

两点注意:
1.表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。

2.完整的表达式要被 包含,注意这个字符不是常用的单引号,在 Esc 键下边。

1)算术运算符
先来看一个使用算术运算符的例子

#!/bin/sh  
a=10  
b=20  
val=`expr $a + $b`  
echo "a + b : $val"  
val=`expr $a - $b`  
echo "a - b : $val"  
val=`expr $a \* $b`  
echo "a * b : $val"  
val=`expr $b / $a`  
echo "b / a : $val"  
val=`expr $b % $a`  
echo "b % a : $val"  
if [ $a == $b ]  
then  
   echo "a is equal to b"  
fi  
if [ $a != $b ]  
then  
   echo "a is not equal to b"  
fi  

运行结果:

a + b : 30
a - b : -10
a * b : 200
b / a : 2
b % a : 0
a is not equal to b

注意:乘号(*)前边必须加反斜杠()才能实现乘法运算;if…then…fi 是条件语句,后续将会讲解。

ymXHHg.png

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

先来看一个关系运算符的例子:

#!/bin/sh  
a=10  
b=20  
if [ $a -eq $b ]  
then  
   echo "$a -eq $b : a is equal to b"  
else  
   echo "$a -eq $b: a is not equal to b"  
fi  
if [ $a -ne $b ]  
then  
   echo "$a -ne $b: a is not equal to b"  
else  
   echo "$a -ne $b : a is equal to b"  
fi  
if [ $a -gt $b ]  
then  
   echo "$a -gt $b: a is greater than b"  
else  
   echo "$a -gt $b: a is not greater than b"  
fi  
if [ $a -lt $b ]  
then  
   echo "$a -lt $b: a is less than b"  
else  
   echo "$a -lt $b: a is not less than b"  
fi  
if [ $a -ge $b ]  
then  
   echo "$a -ge $b: a is greater or  equal to b"  
else  
   echo "$a -ge $b: a is not greater or equal to b"  
fi  
if [ $a -le $b ]  
then  
   echo "$a -le $b: a is less or  equal to b"  
else  
   echo "$a -le $b: a is not less or equal to b"  
fi  

运行结果:

10 -eq 20: a is not equal to b
10 -ne 20: a is not equal to b
10 -gt 20: a is not greater than b
10 -lt 20: a is less than b
10 -ge 20: a is not greater or equal to b
10 -le 20: a is less or  equal to b

ymjev6.png

3)布尔运算符
先来看一个布尔运算符的例子:

#!/bin/sh  
a=10  
b=20  
if [ $a != $b ]  
then  
   echo "$a != $b : a is not equal to b"  
else  
   echo "$a != $b: a is equal to b"  
fi  
if [ $a -lt 100 -a $b -gt 15 ]  
then  
   echo "$a -lt 100 -a $b -gt 15 : returns true"  
else  
   echo "$a -lt 100 -a $b -gt 15 : returns false"  
fi  
if [ $a -lt 100 -o $b -gt 100 ]  
then  
   echo "$a -lt 100 -o $b -gt 100 : returns true"  
else  
   echo "$a -lt 100 -o $b -gt 100 : returns false"  
fi  
if [ $a -lt 5 -o $b -gt 100 ]  
then  
   echo "$a -lt 100 -o $b -gt 100 : returns true"  
else  
   echo "$a -lt 100 -o $b -gt 100 : returns false"  
fi  

运行结果:

10 != 20 : a is not equal to b
10 -lt 100 -a 20 -gt 15 : returns true
10 -lt 100 -o 20 -gt 100 : returns true
10 -lt 5 -o 20 -gt 100 : returns false

ymjwVg.png

4)字符串运算符
先来看一个例子:

#!/bin/sh  
a="abc"  
b="efg"  
if [ $a = $b ]  
then  
   echo "$a = $b : a is equal to b"  
else  
   echo "$a = $b: a is not equal to b"  
fi  
if [ $a != $b ]  
then  
   echo "$a != $b : a is not equal to b"  
else  
   echo "$a != $b: a is equal to b"  
fi  
if [ -z $a ]  
then  
   echo "-z $a : string length is zero"  
else  
   echo "-z $a : string length is not zero"  
fi  
if [ -n $a ]  
then  
   echo "-n $a : string length is not zero"  
else  
   echo "-n $a : string length is zero"  
fi  
if [ $a ]  
then  
   echo "$a : string is not empty"  
else  
   echo "$a : string is empty"  
fi  

运行结果:

abc = efg: a is not equal to b
abc != efg : a is not equal to b
-z abc : string length is not zero
-n abc : string length is not zero
abc : string is not empty

ymjbM6.png

5)文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性。

例如,变量 file 表示文件“/var/www/tutorialspoint/unix/test.sh”,它的大小为100字节,具有 rwx 权限。下面的代码,将检测该文件的各种属性:

#!/bin/sh  
file="/var/www/tutorialspoint/unix/test.sh"  
if [ -r $file ]  
then  
   echo "File has read access"  
else  
   echo "File does not have read access"  
fi  
if [ -w $file ]  
then  
   echo "File has write permission"  
else  
   echo "File does not have write permission"  
fi  
if [ -x $file ]  
then  
   echo "File has execute permission"  
else  
   echo "File does not have execute permission"  
fi  
if [ -f $file ]  
then  
   echo "File is an ordinary file"  
else  
   echo "This is sepcial file"  
fi  
if [ -d $file ]  
then  
   echo "File is a directory"  
else  
   echo "This is not a directory"  
fi  
if [ -s $file ]  
then  
   echo "File size is zero"  
else  
   echo "File size is not zero"  
fi  
if [ -e $file ]  
then  
   echo "File exists"  
else  
   echo "File does not exist"  
fi  

运行结果:

File has read access
File has write permission
File has execute permission
File is an ordinary file
This is not a directory
File size is zero
File exists

ymvEdg.png

5.5.3 Shell字符串

字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟PHP类似。

1)单引号

str='this is a string'

单引号字符串的限制:
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
单引号字串中不能出现单引号(对单引号使用转义符后也不行)。

2)双引号

your_name='qinjx'
str="Hello, I know your are \"$your_name\"! \n"

双引号的优点:
双引号里可以有变量
双引号里可以出现转义字符

3)拼接字符串

your_name="qinjx"
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1

4)获取字符串长度
复制纯文本新窗口

string="abcd"
echo ${#string} #输出 4

5)提取子字符串

string="alibaba is a great company"
echo ${string:1:4} #输出liba

6)查找子字符串

string="alibaba is a great company"
echo `expr index "$string" is`

5.5.4 Shell数组

Shell在编程方面比Windows批处理强大很多,无论是在循环、运算。

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

1)定义数组
在Shell中,用括号来表示数组,数组元素用“空格”符号分割开。定义数组的一般形式为:

array_name=(value1 ... valuen)

例如:

array_name=(value0 value1 value2 value3)

或者

array_name=(
value0
value1
value2
value3
)

还可以单独定义数组的各个分量:

array_name[0]=value0
array_name[1]=value1
array_name[2]=value2

可以不使用连续的下标,而且下标的范围没有限制。

2)读取数组

读取数组元素值的一般格式是:

${array_name[index]}

例如:

valuen=${array_name[2]}

举个例子:

#!/bin/sh
NAME[0]="Zara"
NAME[1]="Qadir"
NAME[2]="Mahnaz"
NAME[3]="Ayan"
NAME[4]="Daisy"
echo "First Index: ${NAME[0]}"
echo "Second Index: ${NAME[1]}"

运行脚本,输出:

$./test.sh
First Index: Zara
Second Index: Qadir

使用@ 或 * 可以获取数组中的所有元素,例如:

${array_name[*]}
${array_name[@]}

举个例子:

#!/bin/sh
NAME[0]="Zara"
NAME[1]="Qadir"
NAME[2]="Mahnaz"
NAME[3]="Ayan"
NAME[4]="Daisy"
echo "First Method: ${NAME[*]}"
echo "Second Method: ${NAME[@]}"

运行脚本,输出:

$./test.sh
First Method: Zara Qadir Mahnaz Ayan Daisy
Second Method: Zara Qadir Mahnaz Ayan Daisy

3)获取数组的长度
获取数组长度的方法与获取字符串长度的方法相同,例如:

# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}

5.6 shell命令及相关语句

5.6.1 Shell echo命令

echo是Shell的一个内部指令,用于在屏幕上打印出指定的字符串。命令格式:
echo arg

您可以使用echo实现更复杂的输出格式控制。

1)显示转义字符

echo "\"It is a test\""

结果将是:

"It is a test"

双引号也可以省略。

2)显示变量

name="OK"
echo "$name It is a test"

结果将是:

OK It is a test

同样双引号也可以省略。

如果变量与其它字符相连的话,需要使用大括号({ }):

mouth=8
echo "${mouth}-1-2009"

结果将是:

8-1-2009

3)显示换行

echo "OK!\n"
echo "It is a test"

输出:

OK!
It is a test

4)显示不换行

echo "OK!\c"
echo "It is a test"

输出:

OK!It si a test

5)显示结果重定向至文件

echo "It is a test" > myfile

6)原样输出字符串
若需要原样输出字符串(不进行转义),请使用单引号。例如:

echo '$name\"'

7)显示命令执行结果

echo `date`

结果将显示当前日期

5.6.2 shell printf命令

printf 命令用于格式化输出, 是echo命令的增强版。它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同。

注意:printf 由 POSIX 标准所定义,移植性要比 echo 好。

如同 echo 命令,printf 命令也可以输出简单的字符串:

$printf "Hello, Shell\n"
Hello, Shell
$

printf 不像 echo 那样会自动换行,必须显式添加换行符(\n)。

printf 命令的语法:

printf  format-string  [arguments...]

format-string 为格式控制字符串,arguments 为参数列表。

printf()在C语言入门教程中已经讲到,功能和用法与 printf 命令类似,请查看:C语言格式输出函数printf()详解。

这里仅说明与C语言printf()函数的不同:

1.printf 命令不用加括号
2.format-string 可以没有引号,但最好加上,单引号双引号均可。
3.参数多于格式控制符(%)时,format-string 可以重用,可以将所有参数都转换。
4.arguments 使用空格分隔,不用逗号。

请看下面的例子:

# format-string为双引号  
$ printf "%d %s\n" 1 "abc"  
1 abc  
# 单引号与双引号效果一样   
$ printf '%d %s\n' 1 "abc"   
1 abc  
# 没有引号也可以输出  
$ printf %s abcdef  
abcdef  
# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用  
$ printf %s abc def  
abcdef  
$ printf "%s\n" abc def  
abc  
def  
$ printf "%s %s %s\n" a b c d e f g h i j  
a b c  
d e f  
g h i  
j  
# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替  
$ printf "%s and %d \n"   
and 0  
# 如果以 %d 的格式来显示字符串,那么会有警告,提示无效的数字,此时默认置为 0  
$ printf "The first program always prints'%s,%d\n'" Hello Shell  
-bash: printf: Shell: invalid number  
The first program always prints 'Hello,0'  
$  

<font color=#DD0000>注意:根据POSIX标准,浮点格式%e、%E、%f、%g与%G是“不需要被支持”。这是因为awk支持浮点预算,且有它自己的printf语句。这样Shell程序中需要将浮点数值进行格式化的打印时,可使用小型的awk程序实现。然而,内建于bash、ksh93和zsh中的printf命令都支持浮点格式。</font>

5.6.3 Shell if else语句

if 语句通过关系运算符判断表达式的真假来决定执行哪个分支。Shell 有三种 if … else 语句:
if … fi 语句;
if … else … fi 语句;
if … elif … else … fi 语句。

1) if … else 语句
if … else 语句的语法:

if [ expression ]
then
   Statement(s) to be executed if expression is true
fi

如果 expression 返回 true,then 后边的语句将会被执行;如果返回 false,不会执行任何语句。
最后必须以 fi 来结尾闭合 if,fi 就是 if 倒过来拼写,后面也会遇见。

注意:expression 和方括号([ ])之间必须有空格,否则会有语法错误。

举个例子:

#!/bin/sh  
a=10  
b=20  
if [ $a == $b ]  
then  
   echo "a is equal to b"  
fi  
if [ $a != $b ]  
then  
   echo "a is not equal to b"  
fi  

运行结果:

a is not equal to b

2) if … else … fi 语句
if … else … fi 语句的语法:

if [ expression ]
then
   Statement(s) to be executed if expression is true
else
   Statement(s) to be executed if expression is not true
fi

如果 expression 返回 true,那么 then 后边的语句将会被执行;否则,执行 else 后边的语句。

举个例子:

#!/bin/sh  
a=10  
b=20  
if [ $a == $b ]  
then  
   echo "a is equal to b"  
else  
   echo "a is not equal to b"  
fi  

执行结果:

a is not equal to b

3) if … elif … fi 语句

if … elif … fi 语句可以对多个条件进行判断,语法为:

if [ expression 1 ]
then
   Statement(s) to be executed if expression 1 is true
elif [ expression 2 ]
then
   Statement(s) to be executed if expression 2 is true
elif [ expression 3 ]
then
   Statement(s) to be executed if expression 3 is true
else
   Statement(s) to be executed if no expression is true
fi

哪一个 expression 的值为 true,就执行哪个 expression 后面的语句;如果都为 false,那么不执行任何语句。

举个例子:

#!/bin/sh  
a=10  
b=20  
if [ $a == $b ]  
then  
   echo "a is equal to b"  
elif [ $a -gt $b ]  
then  
   echo "a is greater than b"  
elif [ $a -lt $b ]  
then  
   echo "a is less than b"  
else  
   echo "None of the condition met"  
fi  

运行结果:

a is less than b
if ... else 语句也可以写成一行,以命令的方式来运行,像这样:
if test $[2*3] -eq $[1+5]; then echo 'The two numbers are equal!'; fi;  

if … else 语句也经常与 test 命令结合使用,如下所示:

num1=$[2*3]  
num2=$[1+5]  
if test $[num1] -eq $[num2]  
then  
    echo 'The two numbers are equal!'  
else  
    echo 'The two numbers are not equal!'  
fi  

输出:

The two numbers are equal!

test 命令用于检查某个条件是否成立,与方括号([ ])类似。

5.6.4 Shell case esac语句

case … esac 与其他语言中的 switch … case 语句类似,是一种多分枝选择结构。

case 语句匹配一个值或一个模式,如果匹配成功,执行相匹配的命令。case语句格式如下:

case 值 in
模式1)
    command1
    command2
    command3
    ;;
模式2)
    command1
    command2
    command3
    ;;
*)
    command1
    command2
    command3
    ;;
esac

case工作方式如上所示。取值后面必须为关键字 in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。;; 与其他语言中的 break 类似,意思是跳到整个 case 语句的最后。

取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。

下面的脚本提示输入1到4,与每一种模式进行匹配:

echo 'Input a number between 1 to 4'  
echo 'Your number is:\c'  
read aNum  
case $aNum in  
    1)  echo 'You select 1'  
    ;;  
    2)  echo 'You select 2'  
    ;;  
    3)  echo 'You select 3'  
    ;;  
    4)  echo 'You select 4'  
    ;;  
    *)  echo 'You do not select a number between 1 to 4'  
    ;;  
esac  

输入不同的内容,会有不同的结果,例如:

Input a number between 1 to 4
Your number is:3
You select 3

再举一个例子:

#!/bin/bash  
option="${1}"  
case ${option} in  
   -f) FILE="${2}"  
      echo "File name is $FILE"  
      ;;  
   -d) DIR="${2}"  
      echo "Dir name is $DIR"  
      ;;  
   *)   
      echo "`basename ${0}`:usage: [-f file] | [-d directory]"  
      exit 1 # Command to come out of the program with status 1  
      ;;  
esac  

运行结果如下:

$./test.sh
test.sh: usage: [ -f filename ] | [ -d directory ]

$ ./test.sh -f index.htm
File name is index.htm

$ ./test.sh -d unix
Dir name is unix

Related posts

Leave a Comment