参考:《Shell从入门到精通》
参考:Shell 编程快速入门
参考:Shell 教程
一、Shell
Shell脚本(英语:Shell script)是一种电脑程序与文本文件,内容由一连串的shell命令组成,由Unix shell扮演命令行解释器的角色,经由Unix Shell直译其内容后运作。
Shell的用处:
- shell简单、灵活、高效,特别适合处理一些系统管理方面的小问题
- shell可以实现自动化管理,让系统管理员的工作变得容易、简单、高效
- shell脚本可移植性好,在unix/linux系统中可灵活移植,几乎不用任何设置就能正常运行
- shell脚本可轻松方便读取和修改源代码,不需要编译
- 资源密集型的任务,尤其在需要考虑效率时(比如排序,hash 等)
- 需要处理大任务的数学操作,尤其是浮点运算,精确运算,或者复杂的算术运算(这种情况一般使用C++或FORTRAN 来处理)
- 有跨平台移植需求(一般使用C 或Java)
- 复杂的应用,在必须使用结构化编程的时候(需要变量的类型检查,函数原型,等等)
- 对于影响系统全局性的关键任务应用。
- 对于安全有很高要求的任务,比如你需要一个健壮的系统来防止入侵,破解,恶意破坏等等.
- 需要直接操作系统硬件、需要产生或操作图形化界面 GUI
- 私人的,闭源的应用(shell 脚本把代码就放在文本文件中,全世界都能看到)
二、Shell环境
Linux
Linux默认安装就带了shell解释器。
Mac OS
Mac OS不仅带了sh、bash这两个最基础的解释器,还内置了ksh、csh、zsh等不常用的解释器。
Windows上的模拟器
windows出厂时没有内置shell解释器,需要自行安装,为了同时能用grep,awk,curl等工具,最好装一个cygwin或者mingw来模拟linux环境。
即Bourne shell,POSIX(Portable Operating System Interface)标准的shell解释器,它的二进制文件路径通常是/bin/sh,由Bell Labs开发。
bash
Bash是Bourne shell的替代品,属GNU Project,二进制文件路径通常是/bin/bash。在CentOS里,/bin/sh是一个指向/bin/bash的符号链接,但在Mac OS上不是,/bin/sh和/bin/bash是两个不同的文件,尽管它们的大小只相差100字节左右,业界通常混用bash、sh、和shell。
三、Shell的解释和执行
- #!/bin/sh
- #!/bin/bash
- #!/bin/csh
- #!/bin/tcsh
- #!/bin/ksh
1. 美元符号
美元符号表示变量替换,即用其后面指定的变量的值来代替变量。反斜线“\”为转义字符,转义字符告诉shell不要对其后面的那个字符进行特殊处理,只是当做普通字符。而shell下的引号情况比较复杂,分为三种:双引号("),单引号(')和倒引号(`)。他们的作用都不尽相同,以下一一说明。
2. 双引号(")
由双引号括起来的字符,除$,倒引号(`)和反斜线(\)仍保留其特殊功能外,其余字符均作为普通字符对待。
3. 单引号(')
由单引号括起来的字符都作为普通字符出现。
4. 倒引号(`)
由倒引号括起来的字符串被shell解释为命令行,在执行时,shell会先执行该命令,并以它的标准输出结果取代整个引号部分。
示例1的代码及输出如下:
#echo "My current directory is `pwd` and logname is $LOGNAME"
My current directory is /root and logname is root示例2的代码及输出如下:
#echo "My current directory is `pwd` and logname is \$LOGNAME"
My current directory is /root and logname is $LOGNAME示例3的代码及输出如下:
#echo 'My current directory is `pwd` and logname is $LOGNAME'【单引号里面的内容不变】
My current directory is `pwd`and logname is $LOGNAME
五、Shell中的变量
variable=value(注:等号左右不能有空格)2.可以在同一行中对多个变量赋值
a=5 b="a string"3.使用变量
$variable 或 ${variable}4.只读变量
myUrl="http://www.w3cschool.cc" readonly myUrl myUrl="http://www.baidu.com"运行脚本,结果如下:
/bin/sh: NAME: This variable is read only.5.删除变量
1) 局部变量 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
2) 环境变量 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
3) shell变量 shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行
7. 变量的通配符
假设有变量 var=http://www.aaa.com/123.htm
1. # 号截取,从左边开始删除第一个匹配到的字符及左边的字符,保留右边字符。
echo ${var#*//}
# 号是运算符,*// 表示 // 号及左边的所有字符,即删除 http://
结果是 :www.aaa.com/123.htm
2. ## 号截取,从左边开始删除最后一个匹配到的字符及左边的字符,保留右边字符。
##*/}
##*/ 表示从左边开始删除最后(最右边)一个 / 号及左边的所有字符,即删除 http://www.aaa.com/
结果是 123.htm
3. %号截取,删除从右开始第一个匹配到的字符及右边字符,保留左边字符
var%/*}
%/* 表示从右边开始,删除第一个 / 号及右边的字符
结果是:http://www.aaa.com
4. %% 号截取,删除从右开始最后一个匹配到的字符及右边字符,保留左边字符
%% %%/* 表示从右边开始,删除最后(最左边)一个 / 号及右边的字符结果是:http:
5. :从左边第几个字符开始,及字符的个数
:05}
其中的 0 表示左边第一个字符开始,5 表示字符的总个数。
6. 从左边第几个字符开始,一直到结束。
7 其中的 7 表示左边第8个字符开始,一直到结束。结果是 :www.aaa.com/123.htm
7. 从右边第几个字符开始,及字符的个数
-3 其中的 0-7 表示右边算起第七个字符开始,3 表示字符的个数。结果是:123
8. 从右边第几个字符开始,一直到结束。
表示从右边第七个字符开始,一直到结束。结果是:123.htm
注:(左边的第一个字符是用 0 表示,右边的第一个字符用 0-1 表示)
六、Shell中的数组
array_name=(value0 value1 value2 value3)用括号来表示数组,数组元素用"空格"符号分割开。还可以单独定义数组的各个分量:
array_name[0]=value0 array_name[1]=value1 array_name[n]=valuen2.读取数组
# 取得数组元素的个数 length=${#array_name[@]} # 或者 length=${#array_name[*]} # 取得数组单个元素的长度 lengthn=${#array_name[n]}
七、Shell中的函数和运算符
7.1 函数语法形式
function name { commands return }或
name () { commands return }
参数处理 | 说明 |
---|---|
$# | 传递到脚本或函数的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数。 如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。 |
$$ | 脚本运行的当前进程ID号 |
后台运行的最后一个进程的ID号 | |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。 |
显示Shell使用的当前选项,与set命令功能相同。 | |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
7.2 表达式计算工具
#!/bin/bash val=`expr 2 + 2` echo "两数之和为 : $val"表达式和运算符之间要 有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。完整的表达式要被 ` ` 包含,注意这个字符不是常用的单引号,在 Esc 键下边。
7.3 算数运算符
说明 | 举例 | |
---|---|---|
+ | 加法 | `expr $a + $b` 结果为30。 |
减法 | `expr $a - $b` 结果为 -10。 | |
* | 乘法 | `expr $a \* $b` 结果为 200。 |
除法 | `expr $b / $a` 结果为2。 | |
% | 取余 | `expr $b % $a` 结果为0。 |
赋值 | a=$b 将把变量 b 的值赋给 a。 | |
== | 相等。用于比较两个数字,相同则返回 true。 | [ $a == $b ] 返回false。 |
不相等。用于比较两个数字,不相同则返回 true。 | [ $a != $b ] 返回 true。 |
7.4关系运算符
检测两个数是否相等,相等返回 true。 | [ $a -eq $b ] 返回false。 | |
检测两个数是否相等,不相等返回 true。 | [ $a -ne $b ] 返回 true。 | |
-gt | 检测左边的数是否大于右边的,如果是,则返回 true。 | [ $a -gt $b ] 返回 false。 |
检测左边的数是否小于右边的,如果是,则返回 true。 | [ $a -lt $b ] 返回 true。 | |
-ge | 检测左边的数是否大于等于右边的,如果是,则返回 true。 | [ $a -ge $b ] 返回 false。 |
检测左边的数是否小于等于右边的,如果是,则返回 true。 | [ $a -le $b ] 返回 true。 |
7.5 布尔运算符
非运算,表达式为 true 则返回 false,否则返回 true。 | [ ! false ] 返回 true。 | |
或运算,有一个表达式为 true 则返回 true。 | [ $a -lt 20 -o $b -gt 100 ] 返回true。 | |
-a | 与运算,两个表达式都为 true 才返回 true。 | [ $a -lt 20 -a $b -gt 100 ] 返回false。 |
7.6逻辑运算符
逻辑的 AND | [[ $a -lt 100 && $b -gt 100 ]] 返回 false |
逻辑的 OR | [[ $a -lt 100 || $b -gt 100 ]] 返回 true |
7.7字符串运算符
[ $a = $b ] 返回 false。 | ||
[ $a != $b ] 返回true。 | ||
-z | 检测字符串长度是否为0,为0返回 true。 | [ -z $a ] 返回 false。 |
检测字符串长度是否为0,不为0返回 true。 | [ -n $a ] 返回 true。 | |
str | 检测字符串是否为空,不为空返回 true。 | [ $a ] 返回true。 |
7.8文件测试运算符
-b file | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] 返回 false。 |
---|---|---|
检测文件是否是字符设备文件,如果是,则返回 true。 | [ -c $file ] 返回false。 | |
-d file | 检测文件是否是目录,如果是,则返回 true。 | [ -d $file ] 返回 false。 |
检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | [ -f $file ] 返回true。 | |
-g file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | [ -g $file ] 返回false。 |
检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | [ -k $file ] 返回false。 | |
-p file | 检测文件是否是有名管道,如果是,则返回 true。 | [ -p $file ] 返回false。 |
检测文件是否设置了 SUID 位,如果是,则返回 true。 | [ -u $file ] 返回false。 | |
-r file | 检测文件是否可读,如果是,则返回 true。 | [ -r $file ] 返回true。 |
检测文件是否可写,如果是,则返回 true。 | [ -w $file ] 返回true。 | |
-x file | 检测文件是否可执行,如果是,则返回 true。 | [ -x $file ] 返回true。 |
检测文件是否为空(文件大小是否大于0),不为空返回 true。 | [ -s $file ] 返回true。 | |
-e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] 返回true。 |
八、Shell中的逻辑控制语句
1. if 语句
if commands; then commands elif commands; then commands else commands fi
for var in item1 item2 ... itemN do command1 command2 ... commandN done当变量值在列表里,for循环即 执行一次所有命令,使用变量名获取列表中的当前取值。命令可为任何有效的shell命令和语句。in列表可以包含替换、字符串和文件名。in列表是可选的,如果不用它,for循环使用命令行的位置参数。
while condition do command done
until condition do command done条件可为任意测试条件,测试发生在 循环末尾,因此循环 至少执行一次—请注意这一点。
case 值 in 模式1) command1 command2 ... commandN ;; 模式2) command1 command2 ... commandN ;; esaccase工作方式如上所示。取值后面必须为单词 in,每一模式必须以 右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。
九、Shell中的测试语句
或
[ expression ]
正则表达式和[[ expression ]]
#!/bin/bash # test-integer2: evaluate the value of an integer. INT=-5 if [[ "$INT" =~ ^-?[0-9]+$ ]]; then if [ $INT -eq 0 ]; then echo "INT is zero." else if [ $INT -lt 0 ]; then echo "INT is negative." else echo "INT is positive." fi if [ $((INT % 2)) -eq 0 ]; then echo "INT is even." else echo "INT is odd." fi fi else echo "INT is not an integer." >&2 exit 1 fi
整数复合命令(( ))
#!/bin/bash # test-integer2a: evaluate the value of an integer. INT=-5 if [[ "$INT" =~ ^-?[0-9]+$ ]]; then if ((INT == 0)); then echo "INT is zero." else if ((INT < 0)); then echo "INT is negative." else echo "INT is positive." fi if (( ((INT % 2)) == 0)); then echo "INT is even." else echo "INT is odd." fi fi else echo "INT is not an integer." >&2 exit 1 fi
十、Shell中的输入和交互
echo -n "Please enter an integer -> " read var echo "var = '$var'"给多个变量赋值:
echo -n "Enter one or more values > " read var1 var2 echo "var1 = '$var1'" echo "var2 = '$var2'"若没有提供变量名,shell 变量 REPLY 会包含数据行
echo -n "Enter one or more values > " read echo "REPLY = '$REPLY'"read选项:read [-options] [variable...]
- -a array:把输入赋值到数组 array 中,从索引号零开始
- -d delimiter:用字符串 delimiter 中的第一个字符指示输入结束,而不是一个换行符
- -e:使用 Readline 来处理输入。这使得与命令行相同的方式编辑输入
- -n num:读取 num 个输入字符,而不是整行
- -p prompt:为输入显示提示信息,使用字符串 prompt
- -r:Raw mode. 不把反斜杠字符解释为转义字符
- -s:Silent mode. 不会在屏幕上显示输入的字符。当输入密码和其它确认信息的时候,这会很有帮助
- -t seconds:超时. 几秒钟后终止输入。read 会返回一个非零退出状态,若输入超时
- -u fd:使用文件描述符 fd 中的输入,而不是标准输入
#!/bin/bash if read -t 10 -sp "Enter secret pass phrase > " secret_pass; then echo -e "\nSecret pass phrase = '$secret_pass'" else echo -e "\nInput timed out" >&2 exit 1 fi输入校正
#!/bin/bash invalid_input () { echo "Invalid input '$REPLY'" >&2 exit 1 } read -p "Enter a single item > " # input is empty (invalid) [[ -z $REPLY ]] && invalid_input # input is multiple items (invalid) (( $(echo $REPLY | wc -w) > 1 )) && invalid_input # is input a valid filename? if [[ $REPLY =~ ^[-[:alnum:]\._]+$ ]]; then echo "'$REPLY' is a valid filename." if [[ -e $REPLY ]]; then echo "And file '$REPLY' exists." else echo "However,file '$REPLY' does not exist." fi # is input a floating point number? if [[ $REPLY =~ ^-?[[:digit:]]*\.[[:digit:]]+$ ]]; then echo "'$REPLY' is a floating point number." else echo "'$REPLY' is not a floating point number." fi # is input an integer? if [[ $REPLY =~ ^-?[[:digit:]]+$ ]]; then echo "'$REPLY' is an integer." else echo "'$REPLY' is not an integer." fi else echo "The string '$REPLY' is not a valid filename." fi菜单驱动
#!/bin/bash # read-menu: a menu driven system information program clear echo " Please Select: 1. Display System Information 2. Display Disk Space 3. Display Home Space Utilization 0. Quit " read -p "Enter selection [0-3] > " if [[ $REPLY =~ ^[0-3]$ ]]; then if [[ $REPLY == 0 ]]; then echo "Program terminated." exit fi if [[ $REPLY == 1 ]]; then echo "Hostname: $HOSTNAME" uptime exit fi if [[ $REPLY == 2 ]]; then df -h exit fi if [[ $REPLY == 3 ]]; then if [[ $(id -u) -eq 0 ]]; then echo "Home Space Utilization (All Users)" du -sh /home/* else echo "Home Space Utilization ($USER)" du -sh $HOME fi exit fi else echo "Invalid entry." >&2 exit 1 fi
十一、文件重定向
1.linux文件描述符
2. 输出重定向
command-line1 [1-n] > file或文件操作符或设备将一条命令执行结果(标准输出,或者错误输出,本来都要打印到屏幕上面的) 重定向其它输出设备(文件,打开文件操作符,或打印机等等)1,2分别是标准输出,错误输出。
#把错误输出,不输出到屏幕,输出到err.txt [zenhobby@zencode shell]$ ls test.sh test1.sh 1>suc.txt 2>err.txt [zenhhobby@zencode shell]$ cat suc.txt err.txt test.sh ls: test1.sh: 没有这个文件和目录 #继续追加把输出写入suc.txt err.txt “>>”追加操作符 [zenhobby@zencode shell]$ ls test.sh test1.sh 1>>suc.txt 2>>err.txt
command > file | 将输出重定向到 file。 |
---|---|
将输入重定向到 file。 | |
command >> file | 将输出以追加的方式重定向到 file。 |
将文件描述符为 n 的文件重定向到 file。 | |
n >> file | 将文件描述符为 n 的文件以追加的方式重定向到 file。 |
将输出文件 m 和 n 合并。 | |
n <& m | 将输入文件 m 和 n 合并。 |
将开始标记 tag 和结束标记 tag 之间的内容作为输入。 |
1、shell遇到”>”操作符,会判断右边文件是否存在,如果存在就先删除,并且创建新文件。不存在直接创建。 无论左边命令执行是否成功。右边文件都会变为空。
2、“>>”操作符,判断右边文件,如果不存在,先创建。以添加方式打开文件,会分配一个文件描述符[不特别指定,默认为1,2]然后,与左边的标准输出(1)或错误输出(2) 绑定。
3、当命令:执行完,绑定文件的描述符也自动失效。0,1,2又会空闲。
4、一条命令启动,命令的输入,正确输出,错误输出,默认分别绑定0,2文件描述符。
5、一条命令在执行前,先会检查输出是否正确,如果输出设备错误,将不会进行命令执行
3.输入重定向
command-line [n] <file或文件描述符&设备命令将默认从键盘获得的输入,改成从文件,或者其它打开文件以及设备输入。执行这个命令,将标准输入0,与文件或设备绑定。将由它进行输入。
[zenhobby@zencode shell]# cat > catfile testing cat file test #这里按下 [ctrl]+d 离开 #从标准输入【键盘】获得数据,然后输出给catfile文件 [zenhobby@zencode shell]$ cat>catfile <test.sh #cat 从test.sh 获得输入数据,然后输出给文件catfile [zenhobby@zencode shell]$ cat>catfile <<eof test a file test! eof #<< 这个连续两个小符号, 他代表的是『结束的输入字符』的意思。这样当空行输入eof字符,输入自动结束,不用ctrl+D
4.重定向到空设备文件
1>/dev/null 2>&11. 标准输入stdin文件描述符为0,标准输出stdout文件描述符为1,标准错误stderr文件描述符为2
2. /dev/null 空设备文件,相当于垃圾桶