SHELL入门教程(7)-编写脚本

shell入门教程(1)-shell基础 - justkk的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/justkk/article/details/43795131

shell入门教程(2)-变量和参数 - justkk的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/justkk/article/details/44081993

shell入门教程(3)-命令编辑 - justkk的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/justkk/article/details/44617445

shell入门教程(4)-作业控制 - justkk的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/justkk/article/details/46801163

SHELL入门教程(5)-算术运算 - justkk的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/justkk/article/details/47025297

SHELL入门教程(6)-环境 - justkk的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/justkk/article/details/47025321

SHELL入门教程(7)-编写脚本 - justkk的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/justkk/article/details/47025397

 

 

1、执行脚本

SHELL也是一个高层编程语言,SHELL程序通常称为脚本,是解释执行的,不需要编译。

脚本的执行需要读取r与执行x权限,可以通过chmod命令添加相应的权限,ls -l查看权限。

ls -l 0.sh

-rwxr-xr-x. 1 root root 10 Jul 8 14:59 0.sh

可以直接通过脚本名称执行,如:0.sh,此时需要读取与执行权限。

也可以使用bash执行,如:bash 0.sh,此时只需要读取权限。

通常,SHELL脚本是在一个单独环境中执行:

1、当前SHELL中的变量需要export之后才能在脚本中使用

2、另外,脚本不会改变当前SHELL环境。

 

2、位置参数

位置参数是一组特殊变量,用来跟踪脚本的参数,其名称只包含数字,如:

$0 表示脚本名称本身,剩余的$1 $2 ..依次表示脚本的参数

可以使用下面的脚本验证:

echo "Script name:  $0"

echo "Number of args passed: $#"

echo "Arguments passed:  $*"

echo "Arg 1=$1,Arg 2=$2, Arg 3=$3"

保存为 0.sh,执行./0.sh a b c

特殊的变量:

$# 表示参数数目,$*$@ 表示所有的参数。

$* $@的区别:$* 把所有参数作为一个整体,而$@表示分离的个体。不带双引号时,两者效果相同。

 

位置参数不能直接使用var=value的方式修改,可以使用shift 命令。

shift 命令依次向左移动所有的参数,每个参数替代目标位置上的参数,同时参数的数目相应减少。

缺省移动一个,可以指定参数,表示移动的数目,如 shift 2

可以使用下面的脚本验证:

echo "Number of args passed: $#"

echo "Arguments passed:  $*"

echo "Arg 1=$1,Arg 2=$2, Arg 3=$3"

shift

echo "Number of args passed: $#"

echo "Arguments passed:  $*"

echo "Arg 1=$1,Arg 2=$2, Arg 3=$3"

保存为 0.sh,执行./0.sh a b c

3、exit

exit 命令退出脚本的执行,后续的命令不再执行。

可以附带一个参数,用来表示整个脚本的退出状态,即执行结果。

如:

echo "over"

exit 1

保存为 0.sh,执行./0.sh

之后在 SHELL 中查看脚本的退出状态,echo $?

4、[[..]] 命令

这个命令用来执行条件判断,可以检查文件属性、字符串、模式、整数等。

格式为 [[ expression ]],注意[[ 后面以及 ]] 前面都需要空格,操作符两边也需要空格。示例:

[[ $a = 3 ]] && echo "a is 3"

c=hi; [[ $c = h* ]] && echo "begin with h"

a=4; [[ $a = +([0-9]) ]] && echo "a number"

字符串操作符

-n string  true if length of string is not zero

-o option  true if option is set

-z string  true if length of string is zero

string1 = string2  true if string1 is equal to string2

string1 != string2  true if string1 is not equal to string2

string = pattern  true if string matches pattern

string != pattern  true if string does not match pattern

string1 < string2  true if string1 less than string2

string1 > string2  true if string1 greater than string2

具体用法参见 man test

 

注意整数的比较,使用-gt -lt等,不能使用大于或小于,那是用来比较字符串的

[[ 13 > 2 ]] && echo "13>2"

[[ 13 -gt 2 ]] && echo "13 greater than 2"

其实整数的比较可以使用((..))命令,更方便,其中的变量不用添加$,也不需要空格,如:

a=13;((a>2))&&echo ">2"

一点注意,两个整数的相等比较,在((..))内部需要使用==,而不是=。单个等号的含义是赋值。

[[..]]内部可以使用==或单个等号来比较。

C语言类似,!表示逻辑“非”。&&表示并且,||表示或者。()用来改变表达式优先级。

(((3+2)*4==20))&&echo "20"

 

test[..]也可以用来执行条件判断,他们同样是SHELL的内置命令。如:

test 1 -eq 1 && echo "ok"

[ 1 -eq 1 ] && echo "ok"

与他们相比,推荐使用[[..]]

总结:

1、整数的比较操作使用((..)),其他的比较操作使用[[..]]

2、使用双等号==判断两个操作数是否相等

5、控制命令

case 执行多个分支检测,如:

case $1 in

a) echo "a";;

b) echo "b";;

*) echo "other";;

esac

for 命令执行循环

for i in 1 2 3

do

        echo $i

done

或者,此时循环变量依次从所有的位置参数取值

for i

do

        echo $i

done

或者,

for((i=1;i<10;i++))

do

        echo $i

done

if ,如:

a=7

if ((a>10));then

        echo "a>10"

elif ((a>5));then

        echo "a>5"

else

        echo "a<=5"

fi

while ,如:

a=1

while((a<10))

do

        echo $a

        ((a++))

done

until ,如:

a=1

until((a==10))

do

        echo $a

        ((a++))

done

break 跳出循环, continue 继续循环

其参数指定循环的层次,缺省为1

如:break 2 跳出2层循环

 

select ,用来显示一个简单的菜单,并等待用户的选择,如:

select i in Choice-A Choice-B Choice-C

do

      echo "You picked selection $REPLY: $i"

done

观察输出结果,显示了SHELL的第三个命令行提示符PS3,其缺省值是#?,可以修改并查看效果。

顺便说一下最后一个命令行提示符PS4,这是跟踪调试提示符,缺省值是+,当打开SHELL的跟踪选项时,每个执行的命令及其参数都会先行显示,行首添加PS4 的值。

关于注释

# 之后直到行尾的内容都作为注释,而不会被SHELL解释执行。

特别注意,脚本第一行的顶头的#! 不是注释,它后面可以指定一个执行程序,表示整个脚本由那个执行程序解释执行。如:

#!/usr/bin/awk -f

{print $0}

6、输入输出命令

echo  输出信息,支持转义字符

\a  bell character

\b  backspace

\c  line without ending newline (remaining arguments ignored)

\f  formfeed

\n  newline

\r  return

\t  tab

\v  vertical tab

\\  backslash

\0x  8-bit character whose ASCII code is the 1-, 2-, or 3-digit octal number x

注意,使用转义字符时需要使用-e选项,如:

echo -e "\044"

echo -e "a\tb\ncd"

 

exec  I/O 重定向,使用文件描述符 0-9 ,如:

exec 5>&1  # 复制文件描述符,此时5等价于1,都对应屏幕

echo a>&5  # 写入描述符5,实际输出到屏幕

exec 1>ff.out  # 重定向描述符1,指向文件ff.out

echo b  # 输出到文件ff.out

exec 1>&5  # 再次重定向描述符1,指向描述符5,对应屏幕

echo c  # 输出到屏幕

exec 1>&-  # 关闭描述符1

echo d  # 报错,因为输出已关闭

 

read  读取输入内容,存入变量,如:

read a  # 等待键盘输入,输入内容放入变量a

read a b  # 等待键盘输入,拆分后放入变量a,b,分隔符通常是空白符,如空格、

IFS 变量,用以指定输入内容的分隔符,如: IFS=,

之后输入内容采用逗号分隔,read a b,如果输入1,2,那么变量ab分别取值12

read 命令缺省从标准输入(即描述符 0 )读取内容,可以通过 -u 选项指定从其他描述符读取,如:

exec 4<ff.out

read -u4 b

REPLY 变量

如果read命令没有指定任何变量,读取内容将放入变量REPLY,如:

read; echo $REPLY

 

read 命令的一个 陷阱 ,查看下面的场景:

unset b c

A="3 4"

echo $A|read b c

echo $b,$c

希望变量bc分别取值34,实际验证结果:变量bc什么也没有。

究其原因,read命令是在子SHELL中执行的,虽然在子SHELL中读到了34,但是不会影响当前SHELL

修改:

unset b c

A="3 4"

read b c <

$A

!

echo $b,$c

 

8、其他

. 命令, source 命令,两者等价,在当前 SHELL 中执行参数指定的脚本, 改变当前的 SHELL 环境

通常用来读取并执行一个环境定义文件,假定文件0.sh的内容如下:

A=3

B=4

比较两种执行方式:./0.sh . ./0.sh

函数,定义形式如下,其中关键字 function 可以省略:

function aa()

{

  echo aa

}

函数定义之后,其调用方式与参数传递方式与普通命令类似,同样可以使用$?检查其退出状态。

aa()

{

        echo hh

        return 1

}

aa  # 调用时可以给函数传递参数,函数内部需要使用位置参数访问

echo $?

 

缺省情况下当前 SHELL 中定义的函数在子 SHELL 中不可用,需要导出: export -f aa
缺省情况下,函数中的变量和调用者是共享的

调用前定义的变量可以在函数中使用,函数中定义的变量在调用结束后依然可见,函数内部定义局部变量时可以使用 local 关键字。

A=1

function aa

{

        local A=3

        B=2

        echo "in function: $A,$B"

}

aa

echo $A,$B

查看已经定义的函数

使用typeset -F -p typeset -f -p,前者只显示函数名称,后者同时显示函数的定义。

 

trap 命令,用来捕获信号,并执行对应的动作,如:

trap 'echo nono' 2  # 捕获中断信号

trap "" 2  # 忽略中断信号

trap 2  # 恢复中断信号的默认处理

特殊的信号0,表示脚本退出。可以捕获这个信号,在脚本退出时执行一些清理动作。

trap 'echo over' 0

..

 

调试脚本

几个有用的选项:

-n  只读入脚本,并不实际执行,只检查语法

-v  读入脚本时显示读入的代码行

-x  执行时显示每个命令及其参数

如:sh -x 0.sh

可以定制 -x 选项显示的提示符,如:export PS4='[$LINENO] ‘

之后将显示每个命令所在的行号

trap 命令也可以用来调试脚本,使用特殊的信号,如:DEBUGERR

trap 'echo error at line $LINENO' ERR

trap 'echo debug line=$LINENO' DEBUG

每个命令执行时都会触发 DEBUG 对应的动作;执行失败时触发 ERR 对应的动作。

 

解析命令行参数

当脚本支持的参数很多时,可以使用 getopts命令,参数解析相对清晰。

while getopts :a:bc: OPT

do

    case $OPT in

        a) echo "$OPT received, arg=$OPTARG" ;;

        b) echo "$OPT received" ;;

        c) echo "$OPT received, arg=$OPTARG" ;;

        :)  echo "$OPTARG needs arg"; exit ;;

        ?) echo "$OPTARG:bad option"; exit ;;

    esac

done

脚本命名为0.sh,尝试执行:

./0.sh -a

./0.sh -a 3

./0.sh -d

 

: 命令,空操作,永远为真

while :

do

    echo a

done

eval 命令,首先执行必要的参数扩展,然后执行扩展之后的命令

a=3

b=a

eval echo \$$b

false 命令,永远为假
pwd 命令,显示当前工作目录
time 命令,显示一个命令的执行耗时,如:

time sleep 3

 

你可能感兴趣的:(shell)