Shell | Shell 编程基础(三)
一、编程基础
-
解释器:
Shell脚本第一行指定解释器必须写#!/bin/bash
,如果没有会默认使用#!/bin/sh
作为解释程序 -
注释:
Shell注释,单行#
,多行<<COMMENT comment line 1 comment line 2 comment line n COMMENT
-
设置执行权限
当前用户增加执行权限chmod u+x ./脚本名.sh
所有用户增加执行权限chmod +x ./脚本名.sh
将脚本路径添加到环境变量中,可以实现任意位置调用,不用使用全路径
-
Bash中的参数扩展
#参数名后面还紧连着其他字符,必须使用{} WORD=car echo ${WORD}s cars #对于使用$9之后的参数也需要使用大括号 echo "Argument 1 is: $1" echo "Argument 10 is: ${10}" #注意:参数名是大小写敏感的 #间接参数扩展 ${!PARAMETER} #上述语句中,被引用的参数不是PARAMETER自身,而是PARAMETER的值 #如:数PARAMETER的值是 TEMP ,则${!PARAMETER}将扩展为参数TEMP的值 PARAMETER=TEMP TEMP="It's indirect" TEMP="It's indirect" > It s indirect #大小写修改(Bash 4.0的新特性): #操作符“^”将参数值的第一个字符改为大写,操作符“,”将参数值的第一个字符改为小写。当使用双重模式(^^和,,)时,参数值的所有字符都将被转换。下面实例中,将当前目录下的所有后缀为txt的文件名转换为小写: mv "$file" "${file,,}" #变量名扩展: ${!PREFIX*} ${!PREFIX@} #这种参数扩展将列出以字符串PREFIX开头的所有变量名。 #字符串移除: ${PARAMETER#PATTERN} ${PARAMETER##PATTERN} ${PARAMETER%PATTERN} ${PARAMETER%%PATTERN} #上述的语法格式中,前两个语句用于移除从参数值的开头匹配指定模式的字符串,而后两个语句与之相反,用于从参数值的末尾匹配指定模式的字符串。操作符“#”和“%”表示将移除匹配指定模式的最短文本,而操作符“##”和“%%”表示移除匹配指定模式的最长文本。 #可能这种参数扩展最常用的用途是提取文件名的一部分 #字符串搜索与替换 ${PARAMETER/PATTERN/STRING} ${PARAMETER//PATTERN/STRING} ${PARAMETER/PATTERN} ${PARAMETER//PATTERN} #/表示只替换一个匹配的字符串 #// 表示替换所有匹配的字符串 #如果没有指定替换字符串,将被删除 #求字符串长度: ${#PARAMETER} MYSTRING="Hello World" echo ${#MYSTRING} > 11 #子字符串扩展 ${PARAMETER:OFFSET} ${PARAMETER:OFFSET:LENGTH} 从指定开始位置截取指定长度字符出啊,省略LENGTH将截取到末尾 MYSTRING="This is used for substring expansion." echo ${MYSTRING:8} >used for substring expansion. #指定LENGTH echo ${MYSTRING:8:10} used for s #使用默认值 ${PARAMETER:-WORD} ${PARAMETER-WORD} #如果参数PARAMAETER未定义或为null时,这种模式会扩展为WORD,否则时参数PARAMAETER #如果省略“:”,只有参数时未定义时才会使用WORD #指定默认值: ${PARAMETER:=WORD} ${PARAMETER=WORD} #使用替代值: ${PARAMETER:+WORD} ${PARAMETER+WORD} #如果参数PARAMETER是未定义的,或其值为空时,这种模式将不扩展任何内容。如果参数PARAMETER是定义的,且其值不为空,这种模式将扩展WORD,而不是扩展为参数PARAMETER的值。
-
Bash的内部变量
#一些常见的Bash内部变量 #用于引用Bash实例的全路径 echo $BASH > /bin/bash #当前用户的home目录 echo $BASH > /root #IFS是内部字段分隔符的缩写。此变量决定当Bash解析字符串时将怎样识别字段,或单词分界线。变量$IFS的默认值是空格(空格、制表符和换行),但可以被修改。请看如下实例: set x y z #使用set命令,将x,y,z赋予位置 参数1,2,3 IFS=":;-" #指定Bash的内部字段分隔符 echo "$*" #扩展特殊参数* x:y:z #操作系统的类型 echo $OSTYPE linux-gnu #$SECONDS变量 脚本已经运行的秒数 #$TMOUT变量 $TMOUT变量被指定了一个非零的值,此值就会被Bash的内部命令read作为默认的超时秒数 #$UID变量 当前用户的账号标识码(ID号)只读变量,不允许修改
6. Bash中的位置参数和特殊参数
- Bash中的位置参数是由除0以外的一个或多个数字表示的参数。
多于一个数字的位置参数在扩展时必须放在大括号中。比如,位置参数10在扩展时使用${10}。
- Bash对一些参数的处理比较特殊。这些参数只能被引用,但不能修改它们的值。这些特殊参数分别是
*
、@
、#
、?
、-
、$
、!
、0
和_
。 - 特殊参数@,也将扩展为从1开始的所有位置参数。但当它的扩展发生在双引号内时,每个参数都扩展为分隔的单词。也就是说,“$@”等价于“$1”、“$2”…。参数@与*之间的区别将在for循环的调用中显现出来。
- 特殊参数#,将扩展为位置参数的个数,用十进制表示:
set one two three # 设置位置参数 echo $# 3
- 特殊参数
?
,将扩展为最近一个在前台执行的命令的退出状态。以使用它来检查Shell脚本是否已成功地执行,通常退出状态0表示命令已经没有任何错误地结束运行。 - 特殊参数
-
,将扩展为当前的选项标志。这些选项是在调用时,或由内部命令set指定,或由Shell自身指定。 - 特殊参数
$
,将扩展为当前Shell的进程号。在一个子Shell中,它扩展为调用Shell的进程号,而不是子Shell的进程号。 - 特殊参数
!
,将扩展为最近一次执行的后台命令的进程号 - 特殊参数
0
,将扩展为Shell或Shell脚本的名称。它是在Shell初始化时设置。 - 特殊参数
_
,在Shell启动时,它被设为开始运行的Shell或Shell脚本的路径。
- 用declare指定变量的类型
-
declare
命令是Bash
的内部命令,用于声明变量和修改变量的属性。它与Bash
的另一个内部命令typeset
的用法和用途完全相同。#如果直接使用declare命令,不指定变量名,将显示所有变量的值 declare #使用-r选项,declare命令将把指定的变量定义为只读变量,这些变量将不能再被赋予新值或被清除 declare -r var=1 使用-i选项,declare命令将把指定的变量定义为整数型变量,赋予整数型变量的任何类型的值都将被转换成整数 使用-x选项,declare命令将把指定的变量通过环境输出到后续命令 使用-p选项,declare命令将显示指定变量的属性和值
- Bash中的数组变量
#间接声明一个数组变量的语法如下所示: ARRAYNAME[INDEX]=value #INDEX是一个正数,或是一个值为正数的算术表达式 #显式声明一个数组变量是使用Bash的内部命令declare declare -a ARRAYNAME #数组变量还可以使用复合赋值的格式: ARRAYNAME=(value1 value2 … valueN) 若要引用数组中某一项的内容,必须要使用花括号“{}”。如果索引编号是“@”或“*”,那么数组的所有成员都将被引用 echo ${linux[@]} > Debian Redhat Suse Fedora arr1=(one two three) echo ${arr1[0]} ${arr1[1]} ${arr1[2]} 使用unset命令可以消除一个数组或数组的成员变量 unset arr1[2] echo ${arr1[@]}
- Bash的算术运算符
- Bash中的算术运算符以及它们的优先级、结合性和值都与C语言相同。
#求幂运算符** let var=5**2 echo $var > 25 #逗号运算符将两个或更多的算术运算连接在一起,所有的运算都被求值,但只有最后一个运算的值被返回。 let var=(2+3, 10-5, 20-6) echo $var 14
- 数字常量
- 默认情况下,Shell算术表达式都是使用十进制数,除非这个数字有特定的前缀或标记。以0开头的常量将被当作八进制数解释,而以“0x”或“0X”开头的数值将被解释为十六进制数。此外,如果数值的格式是BASE#NUMBER,BASE是介于2~64之间的十进制数,表示算术进制基数,比如,BASE是数字12,那么12#NUMBER就表示十二进制数,NUMBER即为此进制中的数值。
let dec=20 #默认为十进制数 let oct=020 #以0开头的八进制数 let hex=0x20 #以0x开头的十六进制数 let bin=2#111 #符号“#”之前的数字2表示此数值为二进制 let base32=32#20 #三十二进制数,数值为20
-
使用算术扩展和let进行算术运算
算术扩展中的运算数只能是整数,算术扩展不能对浮点数进行算术运算。
``` # 变量允许作为运算数 var=$(( $var + 8)) var=$(( var + 8)) echo $var z=$(( x%y )) #求余运算 echo $(( 10>3 )) #符号“>”为比较运算符,运算结果返回1,0 let "i <<= 3" #左移3位并赋值 let "i = i<6 ? i : 6" #问号?前的表达式值为真,取i的值,否则取6 ```
-
使用expr命令
expr
命令是一个用于对表达式进行求值并输出相应结果的命令行工具。它同样也只支持整数运算数,不支持浮点运算数的运算。- 与
let
命令相反,使用expr
命令时,表达式中的运算符左右必须包含空格,如果没有空格,而是将运算符与运算数直接相连,expr
命令将不会对表达式进行求值,而直接输出算术表达式。 - 使用
expr
命令时,对于某些运算符,还需要使用符号“\”进行转义,否则提示语法错误。expr 6 + 8 14 expr 6+8 #运算符左右没有包含空格 6+8 expr 6 * 8 #乘法符号需要使用符号“\”进行转义 expr: syntax error expr 6 \* 8 48 expr 1 \< 2 #运算符“<”同样需要转义 1 expr 2 \> 5 #运算符“>”同样需要转义,还有运算符“<=”、“>=”、“|”和“&” a=15 b=35 expr $a \* $b 525 c='expr $a \* $b' #使用命令替换对变量进行赋值
二、脚本常识
- 退出脚本
- 当它运行完成时,应当返回一个退出状态,用于标识脚本是否成功运行。
- 退出状态码,每一个命令都会返回一个退出状态。一个运行成功的命令会返回一个0。不成功返回一个错误状态码。
- 使用exit命令
exit N
exit命令语句用于从Shell脚本中退出并返回指定的退出状态码N,来指示Shell脚本是否成功结束。- 如果退出状态码N被省略,则将把最后一条运行的命令,的退出状态作为脚本的退出状态码
强烈建议,在你的Shell脚本中对调用的程序进行退出状态检查,并根据退出状态做出相应的处理,当脚本退出运行时,明确地返回一个退出状态码,这对一个完善的Shell脚本来说,是不可或缺的。
- 调试脚本
- Shell脚本调试的主要工作是发现引发脚本错误的原因,以及在脚本中定位发生错误的行。
- 常用方法,使用Bash的
-x
选项启动一个Shell,Shell在执行脚本的过程中把实际执行的每一个命令行显示出来,并且在命令行的行首显示一个“+”
号,“+”
号后面显示的是经过了参数扩展之后的命令行的内容,有助于分析实际执行的是什么命令。bash -x param_underscore.sh + echo 'The $_is /bin/bash' The $_ is /bin/bash + uname -a Linux localhost 2.6.18-238.9.1.el5PAE #1 SMP Tue Apr 12 19:28:32 EDT 2011 i686 i686 i386 GNU/Linux + echo -a -a #上面的输出结果中,前面有“+”号的行是Shell脚本实际执行的命令,其他行则是Shell脚本的打印输出信息。
这一调试功能在自3.0以后的Bash大多数现代版本中可用。
```
#Shell脚本中使用“set -x”和“set +x”命令来调试脚本中的某一段代码。
#不确定脚本param_underscore.sh中的“uname -a”命令将做什么操作,我们可以对脚本中的这段内容做类似如下的调整,只调试这段代码:
set -x
uname –a
set +x
#此时,脚本运行后的内容将类似如下所示:
$ ./param_underscore.sh
The $_ is ./param_underscore.sh
+ uname -a
Linux localhost 2.6.18238.9.1.el5PAE #1 SMP Tue Apr 12 19:28:32 EDT 2011 i686 i686 i386 GNU/Linux
+ set +x
+x
```
-
Bash中还有一个
“-v”
选项,该选项将激活详细输出模式,在这一模式中,由Bash读入的脚本的每一个命令行都将在执行前被打印输出。比如使用-v
选项运行脚本param_ underscore.sh
bash -v param_underscore.sh #通常,将-v选项和-x选项同时使用,可以得到更为详细的脚本调试信息 #x选项虽然使用起来比较方便,但它输出的调试信息仅限于参数扩展之后的每一条实际执行的命令以及行首的一个“+” # -x选项输出的信息只限于参数扩展之后的每一条实际执行的命令以及行首的一个+号,没有代码行号 #用的Bash内部环境变量 $LINENO:表示Shell脚本的当前行号。 $FUNCNAME:它是一个包含了当前在执行调用堆栈中的所有Shell函数名称的数组变量。 ${FUNCNAME[0]}代表当前正在执行的Shell函数的名称,${FUNCNAME[1]}则代表调用函数${FUNCNAME[0]}的函数的名字 $PS4:我们在前面已经讲到,使用Bash的-x选项时,每一条实际执行的命令的行首会显示一个“+”号,而这个“+”号其实就是变量$PS4的默认值。 #增强调试:通过重新定义变量$PS4,就可以增强-x选项的输出信息。例如我们先在命令行提示符下执行如下语句: export PS4='{$LINENO:${FUNCNAME[0]}}' # 然后再使用Bash的-xv选项来调试脚本param_underscore.sh: bash -xv param_underscore.sh # Bash中还有一个执行选项-n,它可用于测试Shell脚本中是否存在语法错误,它会读取脚本中的命令但不会执行它们。在编写完Shell脚本后,实际执行之前,最好首先使用-n选项来测试脚本中是否存在语法错误,这是一个好的习惯。因为 bash -n 脚本名.sh #
- 本编程风格
- 一行不超过80字符
- 保持一致的缩进深度。
- 每个脚本文件都要有注释
- 自定义的变量名或函数名使用小写字母,使用下划线“_”分隔单词