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 $1 in
a) echo "a";;
b) echo "b";;
*) echo "other";;
esac
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
a=7
if ((a>10));then
echo "a>10"
elif ((a>5));then
echo "a>5"
else
echo "a<=5"
fi
a=1
while((a<10))
do
echo $a
((a++))
done
a=1
until((a==10))
do
echo $a
((a++))
done
其参数指定循环的层次,缺省为1
如:break 2 跳出2层循环
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、输入输出命令
\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 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 a # 等待键盘输入,输入内容放入变量a
read a b # 等待键盘输入,拆分后放入变量a,b,分隔符通常是空白符,如空格、
之后输入内容采用逗号分隔,read a b,如果输入1,2,那么变量a和b分别取值1和2。
exec 4<ff.out
read -u4 b
如果read命令没有指定任何变量,读取内容将放入变量REPLY,如:
read; echo $REPLY
unset b c
A="3 4"
echo $A|read b c
echo $b,$c
希望变量b和c分别取值3和4,实际验证结果:变量b和c什么也没有。
究其原因,read命令是在子SHELL中执行的,虽然在子SHELL中读到了3和4,但是不会影响当前SHELL。
修改:
unset b c
A="3 4"
read b c <
$A
!
echo $b,$c
8、其他
通常用来读取并执行一个环境定义文件,假定文件0.sh的内容如下:
A=3
B=4
比较两种执行方式:./0.sh 与 . ./0.sh
function aa()
{
echo aa
}
函数定义之后,其调用方式与参数传递方式与普通命令类似,同样可以使用$?检查其退出状态。
aa()
{
echo hh
return 1
}
aa # 调用时可以给函数传递参数,函数内部需要使用位置参数访问
echo $?
调用前定义的变量可以在函数中使用,函数中定义的变量在调用结束后依然可见,函数内部定义局部变量时可以使用 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 'echo nono' 2 # 捕获中断信号
trap "" 2 # 忽略中断信号
trap 2 # 恢复中断信号的默认处理
特殊的信号0,表示脚本退出。可以捕获这个信号,在脚本退出时执行一些清理动作。
trap 'echo over' 0
..
几个有用的选项:
-n 只读入脚本,并不实际执行,只检查语法
-v 读入脚本时显示读入的代码行
-x 执行时显示每个命令及其参数
如:sh -x 0.sh
可以定制 -x 选项显示的提示符,如:export PS4='[$LINENO] ‘
之后将显示每个命令所在的行号
trap 命令也可以用来调试脚本,使用特殊的信号,如:DEBUG、ERR
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
a=3
b=a
eval echo \$$b
time sleep 3