!脚本执行与调试

1、绝对路径执行,要求文件有执行权限

2、以sh命令执行,不要求文件有执行权限

3、.加空格或source命令执行,脚本将在当前shell中执行

4、检查脚本语法

bash -v test.sh

5、跟踪脚本执行

bash -x test.sh

!环境脚本执行规则

用户登录:/etc/profile、~/.bash_profile、~/.bash_login、~/.profile

用户注销:~/.bash_logout

执行新shell: /etc/bash.bashrc、~/.bashrc

执行script(使用#!/bin/bash):如指定BASH_ENV的值,则执行其指定的启动文件

执行script(使用#!/bin/sh):不调用任何环境脚本

!基本语法

1、脚本程序以#!/bin/bash 开始。以告之系统脚本以何种shell执行。

2、以#开头的行被视为注解,执行时自动忽略。

3、每行不得多于255个字节,可在行末加上反斜杠的方式拆分单行内容到多行中。

例如:

test1=abcdefg\

hij\

klmnopq\

rstuvwxyz

4、多命令以分号分隔时,命令从左至右依次执行。

例如: whoami;w

5、多命令以&&分隔时,命令从左至右按前面命令执行成功后再执行下个命令的原则依次执行。

例如:make && make install

6、多命令以||分隔时,命令从左至右按前面命令执行失败后再执行下个命令的原则依次执行。

7、多个命令以分号分隔放置()中,则会启用子shell并执行。

例如:(mkdir testdir;cd testdir;touch testfile)

8、多个命令以分号分隔放置{}中,则会在当前shell中执行。需要注意的是命令和{}必须有空格间隔,并且最后一条命令也必须加分号。

例如:{ mkdir testdir;cd testdir;touch testfile; }

9、被反引号`括起来的命令或$(命令),shell将直接引用执行结果。

例如:

test=`date`

test=$(date)

10、可以将算术运算式以$[ 算术运算式 ]的形式进行求值。注意算术运算式和[]必须有空格分隔,此方法只支持整型运算。

例如:test=$[ 32 * 17 ]

11、使用$((运算式))的形式也可以进行算术运算求值,并且可以进行布尔运算。

例如:test=$(( 32 * 17 ))

12、使用let 运算式也能进行算术运算,算术式各元素间不得有空格,否则应将算术式用引号括起来。

例如:let ++test

13、任何命令执行后均会返回一个取值范围在0~255之间的整型返回码。主要值含义如下:

    0:正常结束

    1:通用执行错误

    2:误用shell命令

    126:命令不可执行

    127:命令未找到

    128:无效退出参数

    130:被ctrl-c强行中止

    255:退出状态码越界

shell script默认返回最后一条命令的返回码。可以使用exit退出script执行并返回指定的返回码。

例如:exit 15

通过$?这个系统变量可以获取上一条命令的返回码。

例如:

sync

echo $?

14、通过getopts命令获取命令行选项。

语法为:getopts 选项行 选项变量。

选项行由各选项的单一字符组成,如某选项字符需要参数,则在选项字符后加冒号。调用时此选项后无参数的话系统会提示错误,如不希望出现提示则应在选项行最前面加上冒号。系统会将参数存入OPTARG变量。

例如:

#!/bin/bash

while getopts :a:bc opt

do

  case $opt in

    a)

      echo "选项a,后跟参数:$OPTARG";;

    b)

      echo "选项b";;

    c)

      echo "选项c";;

    *)

      ;;

  esac

done

15、利用mktemp建立临时文件

语法为:mktemp 选项 临时文件模板

如果建立成功返回0。

临时文件模板格式形式如:/tmp/test.XXXXXX或/usr/ttt/tttt.XXXXXX,文件名末6个字符必须为XXXXXX.生成成功后会由系统代替为随机6个字符。

选项-q 不产生错误信息。

选项-p 指定建立临时文件的父级目录,父级目录必须已存在。例如:mktemp -p /usr/tmp tf.XXXXXX

选项-t 按环境变量TMPDIR变量中指定的父级目录建立临时文件,如变量未定义,则父级目录为/tmp

选项-d 建立的是临时目录。

直接执行mktemp,会在/tmp目录下建立tmp.XXXXXX形式的临时文件,文件名末6个字符为随机字符。可使用下面方式获取生成的文件名。

tmpfile=$(mktemp)

!变量

1、变量命名可使用英文字母、数字和下划线,必须以英文字母开头,区分大小写。

2、每个shell都拥有自己的变量定义,彼此互不影响。

3、变量直接以等号赋值,注意等号两边不可留空,若等号右侧有空格,赋值要用引号括起来。

例如:

test=1

test='hello world'

4、通过在变量名前加$的方式获取变量的值。或者使用${变量名}的形式,以利于变量名和紧接其后的字母或下划线进行区分。

例如:${test}

5、通过${#变量}的形式获取变量值字串的长度。

例如:test='12345';echo ${#test}   #输出5

6、可以将变量声明为全局变量,全局变量作用范围包含所有shell。命令为:export 变量名

例如:export test

7、使用unset注销变量,需要注意的是注销仅局限于当前shell,即使全局变量也一样。unset -v 变量名 

例如:unset test

8、清空变量的方式:变量名=

例如:test=

9、数组变量

使用变量名=(成员值1 成员值2 ...)的形式赋值,成员值之间以空格分隔。

例如:$testarray=(1 2 'abcd' 38)

数组值获取使用$变量名[下标]的形式,下标从0计数。

例如:$testarray[2]

通过${变量名[@]}或${变量名[*]}可以获取数组所有成员,区别是,前者是各成员单独输出,后者是将所有成员以一个字符串的形式整体输出。

通过${#变量名[@]}或${#变量名[*]}可以获取数组成员数量。例如:${#testarray[@]}。

需要注意unset某个数组成员后,此成员的下标并不移除。

例如:

testarray=(1 2 3 4 5 6)

unset testarray[2]

echo testarray[2]    #无值输出

echo testarray[3]    #输出4

数组可以动态添加成员,例如:testarray=($testarray[@] 7);

如果数组下标也是变量,则应用{}将数组变量括起来,例如:i=1;echo ${testarray[$i]}

10、使用readonly可以声明一个变量为只读属性。

例如:

readonly test  #声明test变量为只读

readonly -f testfunc  #声明testfunc函数为只读

readonly -a testarray  #声明testarray数组为只读

11、bash shell可以通过declare进行变量声明。

例如:

declare -a testarray  #定义一个数组变量

declare -i test    #定义一个整型变量

declare -r test    #定义一个只读变量

declare -t test    #设定变量具有trace属性

declare -x test    #定义一个环境变量

12、shell提供一组命令检测变量是否存在或是否为空,根据检测结果执行对应操作,列表如下:

${变量:-字串}  如果变量存在且有值,返回变量值,否则返回字串内容。

${变量:=字串}  如果变量存在且有值,返回变量值,否则将字串赋给变量,并返回字串内容。

${变量:?字串}  如果变量存在且有值,返回变量值,否则显示将字串作为错误信息显示,并退出script执行。

${变量:+字串}  如果变量存在且有值,返回字串内容,否则返回空值。

注:去掉命令中的:,则只检测变量是否存在,不检测是否为空。

13、变量可以通过${变量:起始位置:截取长度}的形式,过行取子串操作。如果从起始位置取至字串尾,则截取长度可以省略。

例如:

test="123456789"

echo ${test:1:3}    #输出234

echo ${test:2}    #输出3456789

14、变量可以通过命令对其字串值进这行删除替换处理,列表如下:

${变量#匹配式}   根据匹配式从左最短匹配子串并删除。

${变量##匹配式}   根据匹配式从左贪婪匹配子串并删除。

${变量%匹配式}   根据匹配式从右最短匹配子串并删除。

${变量%%匹配式}   根据匹配式从右贪婪匹配子串并删除。

${变量/匹配式/替换串}   根据匹配式从左匹配第一个子串替换成替换串。

${变量//匹配式/替换串}   根据匹配式匹配所有子串替换成替换串。

${变量/#匹配式/替换串}   根据匹配式从左匹配第一个子串替换成替换串。

${变量/匹配式/}   根据匹配式从左匹配第一个子串并删除。

${变量//匹配式/}   根据匹配式从右匹配所有子串并删除。

${变量/#匹配式/}   根据匹配式从左匹配第一个子串并删除。

${变量/%匹配式/}   根据匹配式从右匹配所有子串并删除。

15、利用eval实现动态变量功能,动态变量的变量名由另一个变量的值构成。

示例:

#设n1变量的值为另一个变量的名称

n1="n2"

#给动态变量n2赋值

eval ${n1}=abc

echo "$n1=$n2"    #输出n2=abc

#以动态方式将动态变量n2的值赋给变量t

eval t=\$${n1}

echo "t=$t"   #输出t=abc

!流程控制命令

1、if 条件转向命令

基本格式:

if 命令A

then

  命令

  ...

else

  命令

  ...

fi

嵌套格式:

if 命令A

then

  命令

  ...

elif 命令B

then

  命令

  ...

elif 命令C

then

  命令

  ...

...

...

fi

如果if后面跟的命令执行返回码为0则执行then后面的命令,否则执行else后面的命令。

if和then可以放到一行,前提是if后跟的命令加分号结尾。

例如:

if test -r /etc/passwd; then cat /etc/passwd; fi

2、case条件选择命令

基本格式:

case 变量 in

 匹配串1)

   命令

   ...

   ;;

 匹配串2)

   命令

   ...

   ;;

  ... ...

  ... ...

 匹配串n)

   命令

   ...

   ;;

 *)

   命令

   ...

   ;;

esac

匹配串的格式:

单纯字符串  完全匹配。

*  任意长度的任意字串,包括空串。例如:j*k,匹配jack、jak。

?  单个字符。例如:j??k,匹配jack,但不匹配jak。

[] 字符集.例如:[a-c]OK,匹配aOK、bOK、cOK。

|  匹配多个匹配串。例如:j??k|[a-c]OK|jak。

如果打开extglob选项,则能支持高级匹配。shopt -s extglob

?(匹配串) 匹配0个或1个括号里的匹配串。

*(匹配串) 匹配0个以上括号里的匹配串。

+(匹配串) 匹配1个括号里的匹配串。

@(匹配串) 匹配括号里的匹配串其中的一项。

!(匹配串) 匹配非括号里的匹配串。

3、for循环命令

基本格式1:

for 变量 in IFS分隔符定义的字串

do

  命令

  ...

done

处理基本字串:

for i in 1 2 3 4 5 6 7 8 9

do

  echo $i

done

从文件取串处理:

for i in $(cat /etc/passwd)

do

  IFS_old=IFS

  IFS=':'

  for j in $i

  do

    echo "$j "

  done

  echo "---------------------------"

  IFS=IFS_old

done

以通配符遍历目录:

for i in /etc/*

do

  if [ -f "$i" ]; then

    echo "$i 是文件"

  fi

done

基本格式2:

for ((初始化;结束条件判断;参数变更))

do

  命令

  ...

done

此格式类似C语言,具体定义不再阐述。仅举一例

for ((i=1,j=1;(i+j)<=100;i=i+1;j=j+1))

do

  echo "$i,$j"

done

4、while循环命令

基本格式:

while 条件判断

do

  命令

  ...

done

当条件符合条件判断式时执行循环体。

基本应用:

i=0

while (($i<10))

do

  echo $i

  i=$(($i+1))

done

从文件读取内容:

while read line

do

  echo $line

done < "/etc/passwd"

无限循环:

while :

  do

    sleep 5

  done

5、until循环命令

基本格式:

until 条件判断

do

  命令

  ...

done

当条件不符合条件判断式时执行循环体。

6、select菜单选择命令

基本格式:

select 变量 in IFS定义的分隔符分隔的字串行

do

  命令

  ... ...

done

将字串以IFS定义的分隔符为界分解成若干项,编号后列表显示输出,并将环境变量PS3定义的内容作为选择提示符显示。根据用户选择进行相应的操作。

例如:

PS3="请您选择:"

select sel in "测试 1" "测试选择2" "测试选择 3" "退出选择"

do

  case $sel in

    "测试 1")

      echo "测试 1";;

    "测试选择2")

      echo "测试 2";;

    "测试选择 3")

      echo "测试 3";;

    *)

      break

      ;;

  esac

done

7、break命令,中止并跳出当前循环体。

8、continue命令,直接结束此轮循环,继续下轮循环。

9、条件判断命令

条件判断命令是与流程控制语句配合使用的命令。

命令格式: test 条件判断式

返回条件判断式的值,条件成立返回0,否则返回非0值。

通常使用[ 条件判断式 ]的格式代替test命令,注意条件判断式与[]必须有空格分开。

参与判断的变量必须已初始化且非空。

例如:[ -e ~/.bashrc ]

条件判断支持复合条件判断:

A、与判断

使用&&分隔两条判断命令。

例如:[ -e ~/.bashrc ] && [ ~/.bashrc -nt /etc/passwd ]

B、或判断

使用||分隔两条判断命令。

例如:[ -e ~/.bashrc ] || [ ~/.bashrc -nt /etc/passwd ]

C、非判断

使用!符号进行非判断。

例如: [ ! -e ~/.bashrc ]

条件判断式包括数值判断、字串判断、文件判断和复合判断四类操作,分别定义如下:

A、数值判断

[ n1 -eq n2 ]   #判断n1==n2

[ n1 -ge n2 ]   #判断n1>=n2

[ n1 -gt n2 ]   #判断n1>n2

[ n1 -le n2 ]   #判断n1<=n2

[ n1 -lt n2 ]   #判断n1<n2

[ n1 -ne n2 ]   #判断n1!=n2

注意:数值判断仅支持整型。

B、字串判断

[ str1 = str2 ]   

[ str1 != str2 ]   

[ str1 < str2 ]   

[ str1 > str2 ]   

[ -n str1 ]   #判断str1长度是否非0   

[ -z str1 ]   #判断str1长度是否为0   

注:如果字串有空格,应将其用引号括起来。

C、文件判断

[ -d file ]   #判断目录file是否存在

[ -e file ]   #判断file是否存在

[ -f file ]   #判断文件file是否存在

[ -r file ]   #判断file是否存在并可读

[ -s file ]   #判断file是否存在并非空

[ -w file ]   #判断file是否存在并可写

[ -x file ]   #判断file是否存在并可执行

[ -O file ]   #判断file是否存在并属于当前用户

[ -G file ]   #判断file是否存在且默认组于当前用户所属组是否相同

[ file1 -nt file2 ]   #判断file1是否比file2新

[ file1 -ot file2 ]   #判断file1是否比file2旧

D、复合判断

-a  与操作,例如:[ -e file -a -r file ]

-o  或操作,例如:[ -e file -o -r file ]

  

条件判断可以通过(())使用标准数学运算符进行数值赋值运算和判断。

例如:

test=3

if (( $test * 3 > 1 ));then

  echo $test

else

  echo $(( ++test ))

fi

条件判断可以通过[[]]代替test进行字串比较,同时还提供了正则匹配功能。注意判断式和[[、]]以及运算符之间要用空格分隔。

例如:

test="aaabbb123"

if [[ $test == 'a*' ]];then

  echo ok

fi

!函数

1、基本格式

function 函数名()

{

  命令

  ... ...

}

2、基本用法

关键词function和函数名后的括号可以单独省略,但不可同时省略。

默认以最后执行命令的返回值代表函数的返回码。可以使用return命令立即结束函数执行,并以return后指定的值做为返回码。如果return未指定值,则返回0。

函数需要在首次调用前定义,建议在程序最前面定义。

可以将函数统一定义在单独文件中,再以source或. (注意.后面有空格)的形式被其他程序调用。为了不产生冲突,建议函数包中定义的函数名以下划线开头。

函数内局部变量应以local关键词定义。

函数直接以函数名调用,如需传入参数则在函数名加空格后跟写,多个参数以空格分隔。

函数使用和$n的形式调用传入的位置参数,此时主程序的位置参数不受影响。

函数定义后支持动态调用。

例如:

func.sh

#!/bin/bash

_testfunc()

{

  if (( $1 > $2 ));then

    echo "$1 > $2"

  elif (( $1 == $2 ));then

      echo "$1 = $2"

    else

      echo "$1 < $2"

  fi

  return 10

}

main.sh

#!/bin/bash

. func.sh

_testfunc 32 24

_testfunc 15 15

_testfunc 1 12

#动态调用示例

s="_testfunc"

`$s 12 12`

!常用内置命令

1、echo

显示信息,自动换行

echo -n

取消自动换行

echo -e

字串中控制符生效,比如\n

echo显示的信息如果包含空格则应用单引号或双引号括起来。

用双引号括起来的内容,会对其中的变量取值、运算式计算结果、获取命令执行结果、转义符进行转义后再输出。

单引号对括起来内容不做任何转换直接输出。

例如:echo "it's me" #输出 it's me

test=lykyl;echo '$test' #输出 $test

如果显示信息包括变量则获取变量值后再与字串组合后显示。

例如:test=lykyl;echo "hello $test"  #输出 hello lykyl

如果确实要输出$字符,可以通过转义符或将字串用单引号括起来。

例如:test=lykyl;echo -n 'hello $test';echo "\$test"  #输出 hello $test $test

2、:

回传0

3、eval

解析字串参数,以命令形式执行

4、source

在当前shell中执行指定shell程序。

例如:source func.sh

6、read

read 变量名

获取用户输入,并将输入内容存入命令后面的变量中。如果不指定变量,则默认存入REPLY这个变量中。

read -p '提示信息' 变量名

显示提示信息后,等待用户输入。

read -a 变量名

接受以空格分隔的一组值,存入指定的变量中。

read -r 变量名

read命令默认去掉转义字符前面的反斜杠,例如,输入\n存入变量后只有n。增加-r选项后,read命令将接受转义字符,不做过滤。

read -t 等待时长 变量名

设置read等待输入的时长,超时后read将返回一个非零值。

read -s 变量名

隐藏用户输入内容在屏幕上的回显。

7、exec

exec 命令

执行命令,取代目前shell。

exec < 文件名

转向输入,将通过标准输入读取数据的方式变成从指定文件获取。

8、eval

执行命令后面参数组合成的指令。

例如:

showfile='/etc/passwd'

eval "cat $showfile"

9、expr

返回算术运算式的值,需要注意运算式元素间应以空格分隔,遇到与系统控制符冲突的操作符要加转义。

例如:

expr 1 + 1 

expr 2 \* 3

10、bc

计算器

可以通过bc使shell script处理浮点运算。bc内置变量scale控制小数点后精确位数。

A、简单运算

通过管道将多个运算式以分号分隔后传给bc,从左至右依次执行后将最右侧运算式的值输出。

echo "运算式1;运算式2;..."|bc

例如:

num=33

var=`echo "scale=3;v=5*$num;v3=v/4;v3+1"|bc`

B、复杂运算

通过内联输入重定向进行更复杂运算操作。

例如:

num=33

var=`bc <<EOF

scale=2

v1=15.5

v2=34*v1

v3=$num*v2+3

v1*(v2+v3/2)

EOF

`

!常用系统变量

1、$0

当前执行的shell script文件名(带完整路径)

2、$1 ~ $n

依次存放shell script的命令行参数,数值大于9时必须要用{}括起来,比如${10}。

命令行参数可以通过shift命令进行位移操作,位置参数根据shift命令指定的数值往前移动,如不指定移动值,则移动1次。例如:

#!/bin/bash

echo "所有入参:$@"

while shift

do

  [ -n "$1" ] && echo $1

done

3、$*

将所有命令行参数做为一个字符串存入此变量。

4、$@

将所有命令行参数做为一个字符串数组,每个参数为一个成员变量,存入此变量。

5、$#

命令行参数的个数。

6、$?

上一条命令执行后的返回码。

7、$$

当前执行的shell script进程编号。

8、$!

上一个后台程序的进程编号。

9、$_

script执行时,存放bash的绝对路径。

bash交互时,存放上一个命令最后一个命令行参数。

邮件检测时,存放邮件文件名。