Bash shell 脚本编程全攻略(上篇)

Bash shell 脚本编程全攻略(上篇)

 

什么是shell脚本呢?

当命令不在命令行上执行,而是通过一个文件执行时,该文件就称为shell脚本,脚本以非交互的方式运行。Shell脚本把命令通过一些语法组织起来,便能实现特定的功能。

 

Shell脚本主要运用在系统运维中,主要功能有:

自动化常用命令;

执行系统管理和故障排除;

创建简单的应用程序;

处理文本或文件。

 

Shell脚本创建与执行

Shell脚本通常在编辑器中编写,由命令及其注释组成,注释是跟在井号(#)后面的内容,用来对脚本进行注释。

脚本左上角的第一行会指出由哪个程序来执行脚本中的行。这一行通常称为shbang行,必须为顶端第一行,如由bash来执行则为:

#!/bin/bash

在执行shell脚本时,须先给予执行权力,然后在命令行上指定脚本的路径运行;或是直接运行解释器,将脚本作为解释器的参数运行。

了解shell脚本编程,我们先要了解其一些基本的组成或涉及的基本内容。由于涉及内容较多,一些部分可能不会详细讲解。

 

变量

学习Shell脚本离不开变量,根据生效范围变量主要分为;

局部变量:只对当前shell进程有效;对当前shell之外的其它shell进程包括其子shell均无效。在函数中用local声明的局部变量只能用在函数中。

环境变量:当前shell进程及其子进程有效。

变量赋值

局部变量 VAR_NAME=value 或declare VAR_NAME=value

环境变量 export name=value 或declare -x name=value

显示所有环境变量可以用命令:export或env

只读变量 readonly name=value 或 delcare -r name=value

声明后不能修改和删除。

删除变量 unset name

显示已定义的所有变量 set或declare

变量引用 $name;${name}

shell 会对双引号内的美元符号后的变量执行变量扩展,对于单引号内的则不会。

 

命令替换

Linux命令的输出可以被赋给一个变量,或者通过使用反引号引用命令,在一个字符串中使用该命令的输出。Bash还提供了另一中新语法:无需反引号,只将命令包含在由美元符号开始的一对圆括号中即可。即:

    variable_name=`COMMAND`

    variable_name=$(COMMAND)

范例

Bash shell 脚本编程全攻略(上篇) 

 

数组

Bash中可以创建一维数组。数组允许将一列词放到一个变量名中,例如一列数、一列名称或一列文件。


数组声明(赋值)

数组可以用内置命令decare -a来创建,或者直接给变量名一个下标来创建,如arry_name[0]=5。索引值是从0开始的整数。

如果declare命令带-a和-r选项,将创建一个只读数组。

也可以一次性给全部或部分元素赋值,如:

    arry_name=("apple pie" banana  cat )此时索引从0开始,按序关联。

    arry_name=([0]="apple pie" [2]=cat )

declare,readonly,和local内置命令也可以带-a选项来声明一个数组,-a选项的read命令用来从标准输入读入一组词到数组元素中。

 

关联数组

数组没有上限,索引不必连续(稀疏格式)。还可以使用自定义的模式,如declare -A arry_name=([a]=apple [b]=banana),称为关联数组,此时必须用decare -A来声明赋值才能用。

 

数组引用

要取出数组中的一个元素:${arry_name[index]}。

引用数组中的所有元素:${arry_name[*]}或${arry_name[@]}

数组切片 ${arry_name[@]:offset:num}。

    eg.  ${arry1[@]:1:3} 跳过第一个,取之后的3个元素。

 

数组长度(元素个数)

    ${#arry_name[*]}或${#arry_name[@]}

 

数组删除

    删除整个数组:unset arry_name

    删除单个元素:unset arry_name[index]

 

Shell读取用户输入之read

read是一个内置命令,用于从终端或文件读取输入。read命令读取一个输入行,直至遇到换行符。如果read后未跟变量,读入的行将会赋给内置变量REPLY。也可以用read命令来中断程序的运行,直至用户输入一个回车键。read常用方法与选项为:

read answer

从终端读入一行赋值给变量answer

read first…last

把用户键入的词依次赋给赋给变量fist…,剩余的部分全部保存到变量last中

read -p promt

打印提示符,并等待用户输入

-a arryname

读入一组词,以此赋值给数组arryname

-t timeout

等待用户输入时间,如果在timeout(sec)时间没有输入一个完整的行则退出并返回一个失败状态值。

范例

Bash shell 脚本编程全攻略(上篇) 

 

位置参量与命令行参数

用户可以通过命令行向脚本传递参数,我们用位置参量(或称为位置变量)来引用。例如:$1代表第一个位置参量。位置参量有:

$0

脚本名(脚本路径)

$1…${10}

单独的位置参量

$#

位置参量个数

$*

所有参数,全部参数合为一个字符串

$@

所有参数,全部参数分为单独字符串;$*与$@在加双引号时才有区别

带参数的set命令将重置位置变量,且原来的参量列表就丢失了。要清除所有位置参量,可使用set –,$0始终代表脚本名。

范例:脚本如下

#!/bin/bash

echo $1

echo $2

echo $3

echo ====

for i in $*;do

        echo $i

done

echo ====

for i in $@;do

        echo $i

done

echo ====================

for i in "$*";do

        echo $i

done

echo ====

for i in "$@";do

        echo $i

done

set 1 2 3

echo $1

echo $2

echo $3

set —

echo \$1 is $1

执行结果

Bash shell 脚本编程全攻略(上篇) 

说明:输入3个脚本参数"blue sky”green white给脚本test。

 

算术运算

Shell脚本少不了算术运算。

可以用declare -i定义一个整型变量,如果把一个整型变量赋值给一个字符串,则bash把变量值变为0。

整型变量可以进行直接进行算术运算;内置let命令也允许进行算术操作,且带有丰富的类C操作符,双括号(( ))是let的另一种可选形式。

范例:

Bash shell 脚本编程全攻略(上篇) 

说明:整型变量直接运算时,中间不能有空格,或者用双引号括起来。

let命令是shell的内置命令,可以用来进行整数运算和数值表达式测试。

范例:

Bash shell 脚本编程全攻略(上篇) 

说明:let执行算术运算时,不需要用美元符号$展开变量;如果参数包含空格则需要加引号;双括(( ))可以代替let变量,此时操作符之间可以有空格。let操作符有:

+  –  *  /

加(正号)、 减(负号)、 乘、 除

%

余数(取模)

逻辑非

按位取反

<<  >>

按位左移 、 按位右移

<  >  <=  >=

关系运算符,小于、 大于、 小于等于、 大于等于

==  !=

相等 、不相等

&  |  ^

按位与 、按位或、按位异或

&&  ||

逻辑与 、逻辑或

=  *=  /=  %=  +=  -=  <<=  >>=

&=  ^=  |=

赋值、快捷赋值运算符


算术扩展

Shell通过对一个算术表达式求值并替换结果来进行算术扩展。表达式可以像在双引号中一样来处理,并且可以嵌套。求值算术表达式有以下两种格式:

    $[ expression ]  

    $(( expression ))

范例:

Bash shell 脚本编程全攻略(上篇) 

因也可以把$[ ]当作算术运算的另一种方法。

当执行变量、命令、算术表达式和路径名的扩展时,shell被设计按照指定的顺序来扫描命令行。假设变量没有被引用,处理就将按下面的顺序进行。

1、花括号扩展2、代字符号扩展3、参量扩展4、变量替换5、命令替换6、算术扩展7、词分离8、路径名扩展


expr命令

算术运算还可以采用一种方法:expr命令。expr命令用来求表达式的值,其中便包括部分算术运算。

范例

Bash shell 脚本编程全攻略(上篇) 

说明:用expr时各符号间需要空格隔开,乘号需要加反斜杆转义。


浮点运算

Bash只支持整型运算,但也可以一些其他工具如bc,awk来处理更复杂的运算。

范例

Bash shell 脚本编程全攻略(上篇) 

说明:用echo将表达式通过管道传递给bc,scale变量等于3表示小数点后面为3位小数。

 

条件测试

Bash可以测试两种类型的条件:命令成功或失败,表达式为真或为假。在任何一种类型的测试中都要使用退出状态。退出状态为0表示命令执行成功或表达式为真,非0表示命令执行失败或表达式为假。状态变量“?”中保存的是退出状态值。


条件测试之test与let

单方括号的test命令    通常内置的test命令来测试表达式的值,test命令也可以链接到方括号上。这样,既可以使用单独的test命令,也可以通过把表达式用单括号括起来,来测试表达式的值。在用test命令或单方括号来测试表达式时,表达式中的shell元字符不会被扩展。由于要对变量进行单词分离,因此包含空白字符的字符串必须用引号括起来。

双方括号的test命令    用双方括号[[ ]](内置的test复合命令)来测试表达式的值时,对变量不进行单词分离,但可以通过元字符扩展进行模式匹配。包含空白字符的字符串必须用引号括起来。此时逻辑操作符&&(与)和||(或)代替了与test命令一起使用的-a和-o选项。

test命令操作符(除&&和||外,其他操作符两边及中括号两边都须有空格):

操作符

测试内容

字符串测试

[ string1 = string2 ]

string1等于string2

[ string1 == string2 ]

string1等于string2(可用=代替)

[ string1 != string2 ]

string1不等于string2

[ string ]

string不为空

[ -z string ]

string的长度为0

[ -n string ]

string的长度不为0

逻辑测试

[ string1 -a string2 ]

string1和string2都为真

[ string1 -o string2 ]

string1或string2至少有一个为真

[ ! string ]

字符串不匹配

逻辑测试(复合命令)

[[ pattern1 && pattern2 ]]

pattern1和pattern2都为真

[[ pattern1 || pattern2 ]]

pattern1和pattern2至少有一个为真

[[!pattern ]]

模式不匹配

[[ pattern1 =~ pattern2 ]]

pattern1是否与pattern2匹配(正则表达式匹配)

整数测试

[ int1 -eq int2 ]

Int1等于int2

[ int1 -ne int2 ]

Int1不等于int2

[ int1 -gt int2 ]

Int1大于int2

[ int1 -ge int2 ]

Int1大于或等于int2

[ int1 -lt int2 ]

Int1小于int2

[ int1 -le int2 ]

Int1小于或等于int2

文件测试

[ file1 -nt file2 ]

文件file1比file2新则为真

[ file1 -ot file2 ]

文件file1比file2老则为真

[ file1 -ef file2 ]

文件file1和file2有相同的设备数或i节点数则为真

[ -e file ]

文件是否存在(同-a);此外还有许多文件测试,如

-r/-w/x;-g/-u/-k;-t/-N/-O/-G;

-b/-c/-d/-f/-h(-L)/-p/-s/-S(socket)

范例

Bash shell 脚本编程全攻略(上篇) 

说明:单方括号是test的另一种表示方法,test命令不允许使用通配符,所以单方括号中[LI]不会被展开,linux与[Ll]inux不相等,返回状态为1;当使用复合test命令时可以使用通配符,返回值为真。

Bash shell 脚本编程全攻略(上篇) 

说明:双方括号的复合test,此时支持通配符,不进行变量分离;等号加波浪符(=~),则为正则表达式模式匹配。逻辑操作符用&&代替-a。


let命令和带双圆括号的算术表达式测试

虽然test命令可以测试算术表达式,但由于let命令丰富的类C操作符,我们更愿意使用let命令,此时将表达式放在双圆括号来表示不同含义。

范例

Bash shell 脚本编程全攻略(上篇) 

说明:双圆括号代替let命令来测试算术表达式里的值,此时括号里的变量不需要使用美元符号$展开。

 

条件语句

条件语句之if

条件语句能够根据某个特定的条件是否满足,来选择执行相应的任务。if是最简单的决策形式。if/else语句提供双分支,而if/elif/else语句则提供多分支。

if语句后加一条后面跟一条或一组命令引用或测试表达式,如果该命令或测试表达式的退出状态为0,则执行then到fi之间的语句;如果退出状态为非0,则shell直接跳到fi后面执行命令。双路决策则为退出状态为0则执行then到else之间的语句;否则执行else到fi之间的语句。同理多分支则再次提供一次或多次测试。

if语句格式

Bash shell 脚本编程全攻略(上篇) 

详细为:

单分支

if condition;then

COMMANS(命令组)

fi


双分支

if condition;then

COMMANS(命令组)

else

COMMANS(命令组)

fi


多分支

if condition;then

COMMANS(命令组)

elif condition2;then

COMMANS(命令组)

else

COMMANS(命令组)

fi

条件conditon可为命令、test测试表达式或则双圆括号的算术表达式(( ))。

关键字之间的命令采用缩进格式,是一种惯例,以使程序便于阅读和调试。

if语句可以嵌套,如果嵌套使用if语句,fi总是与最近的if语句配套。同理最好对嵌套if语句进行缩进。

范例:

Bash shell 脚本编程全攻略(上篇) 

说明:反向单引号引用grep命令在/etc/passwd文件中查找$name,由于不需要看grep命令的输出结果,所以用标准错误输出和标准输出都重定向到linux的位容器/dev/null中。

grep命令若找到了$name则退出状态返回0,就执行then后面的语句,否则执行else后面的语句。exit 1显示退出状态1,说明查找失败。


exit命令和变量"?"

exit命令用于终止脚本并返回命令行。传递给exit的参数是一个0~255之间的整数。如果程序返回退出状态0,则表示程序成功退出。否则表示遇到了某种失败。传递给exit的参数保存在变量"?"中。脚本程序默认的退出状态是程序最后一条命令的执行状态。

 

null命令

冒号(:)代表的null命令是shell的一个内置命令,它不做任何工作,只返回退出状态0。null命令有时被放在if后作为一个占位符,这时if命令没有什么事可做,却需要有一条命令放在then后面,否则程序会产生报错信息。null命令常常被用作循环命令的参数,作用是让循环无限执行下去。

 

条件语句之case

case命令是一个多路分支命令,可用来代替if/elif命令。case变量的值与value1,value2等的值逐一比较,知道找到与之匹配的值。如果某个值与case变量匹配,程序就执行该值后面的命令,直到遇到双分号,然后跳到esac后面继续往下执行。

如果没有找到与case便来那个匹配的值,程序就执行默认值"*)"后面的命令,然后跳出。case的表达式里可以用shell通配符,还可以用竖杆将两个值相或。

格式

Bash shell 脚本编程全攻略(上篇) 

详细

case 变量 in

value1)

COMMANDS

;;

value2)

COMMANDS

;;

*)

COMMANDS

;;

esac

范例

Bash shell 脚本编程全攻略(上篇) 

说明:提示用户输入一个选择存入变量choice中,如果choice的值与下面的选项某项匹配则执行相应的命令。


用here文档和case语句生成菜单

here文档经常与case命令结合起来使用。我们可以用here文档生成一个选项菜单显示在屏幕上,要求用户选择一个菜单项,然后用case命令对照选项测试用户的输入以执行相应的命令。

Bash shell 脚本编程全攻略(上篇) 

说明:cat生成here文档,read读入用户的输入保存在choice变量中,其值与那个选项匹配便执行相应的命令。

原创文章,作者:beyond,如若转载,请注明出处:http://www.178linux.com/40785

(1)
beyondbeyond
上一篇 2016-08-29
下一篇 2016-08-29

相关推荐

  • Linux用户及文件权限管理

    Linux,用户,用户组,文件,权限

    Linux干货 2017-10-29
  • 网络管理基础-子网划分及网络配置练习

    1、某公司申请到一个C 类IP 地址,但要连接6 个的子公司,最大的一个子  公司有26 台计算机,每个子公司在一个网段中,则子网掩码应设为?  192.168.100.1 网络位192.168.100 192.168.100.00000000 C类掩码255.255.255.0 192.168.100. 000 00001  …

    Linux干货 2016-09-05
  • 任务计划

                          Linux任务计划 一、任务计划     (1)未来的某一个时间点执行一次某任务:at、batch     (2)周期性运行某…

    2017-05-14
  • keepalived + LVS-NAT 双主互备模型

        实验环境拓扑图:     备注:内网段使用192.168.91.0/24 网段模拟。外网使用192.168.23.0/24网段模拟 1、两节点上关闭防火墙和selinux。 [root@node1 keepalived]# systemctl stop firewalld…

    Linux干货 2016-03-12
  • Linux基础之权限管理(含SUID\SGID\STICKY和ACL)

    概述     Linux系统是一个多人多任务的操作系统,系统上同时可能有很多人登录,每个人都会利用系统上的各类资源完成一定的操作。那么如何合理的规范这些用户的行为,保证资源的合理分配,则就需要用到权限管理的相关内容了。权限管理是Linux系统上最基础,也是最重要的一部分内容,本章就简要说明下权限管理的相关内容。内容主要…

    Linux干货 2016-08-04
  • 软件安装包的管理

    rpm安装包的管理 rpm的数据库( 公共) :/var/lib/rpm 程序包名称及版本 依赖关系 功能说明 包安装后生成的各文件路径及校验码信息 安装 rpm:{-i} -v:显示详细信息 -h:以#显示程序包管理执行进度 rpm -ivh PACKAGE_FILE … [install-options] –test :测试安装,但不真正执行安装过程 –…

    Linux干货 2017-04-23