玩转shell符号
在shell脚本编程中常用的特殊符号 如下:
# ; ;; . , / \\ 'string'| ! $ ${} $? $$ $*
\"string\"* ** ? : ^ $# $@ `command`{} [] [[]] () (())
|| && {xx,yy,zz,...}~ ~&#43; ~- & \\<...> &#43; - %&#61; &#61;&#61; !&#61;
# 井号 (comments)
这几乎是个满场都有的符号&#xff0c;除了先前已经提过的\"第一行\"
#!/bin/bash
井号也常出现在一行的开头&#xff0c;或者位于完整指令之后&#xff0c;这类情况表示符号后面的是注解文字&#xff0c;不会被执行。
# This line is comments.
echo \"a &#61; $a\" # a &#61; 0
由于这个特性&#xff0c;当临时不想执行某行指令时&#xff0c;只需在该行开头加上 # 就行了。这常用在撰写过程中。
#echo \"a &#61; $a\" # a &#61; 0
如果被用在指令中&#xff0c;或者引号双引号括住的话&#xff0c;或者在倒斜线的后面&#xff0c;那他就变成一般符号&#xff0c;不具上述的特殊功能。
~ 帐户的 home 目录
算是个常见的符号&#xff0c;代表使用者的 home 目录&#xff1a;cd ~&#xff1b;也可以直接在符号后加上某帐户的名称&#xff1a;cd ~user或者当成是路径的一部份&#xff1a;~/bin
~&#43; 当前的工作目录&#xff0c;这个符号代表当前的工作目录&#xff0c;她和内建指令 pwd的作用是相同的。
# echo ~&#43;/var/log
~- 上次的工作目录&#xff0c;这个符号代表上次的工作目录。
# echo ~-/etc/httpd/logs
; 分号 (Command separator)
在 shell 中&#xff0c;担任\"连续指令\"功能的符号就是\"分号\"。譬如以下的例子&#xff1a;cd ~/backup ; mkdir startup ;cp ~/.* startup/.
;; 连续分号 (Terminator)
专用在 case 的选项&#xff0c;担任 Terminator 的角色。
case \"$fop\" inhelp) echo \"Usage: Command -help -version filename\";;version) echo \"version 0.1\" ;;esac
. 逗号 (dot,就是“点”)
在 shell 中&#xff0c;使用者应该都清楚&#xff0c;一个 dot 代表当前目录&#xff0c;两个 dot 代表上层目录。
CDPATH&#61;.:~:/home:/home/web:/var:/usr/local
在上行 CDPATH 的设定中&#xff0c;等号后的 dot 代表的就是当前目录的意思。
如果档案名称以 dot 开头&#xff0c;该档案就属特殊档案&#xff0c;用 ls 指令必须加上 -a 选项才会显示。除此之外&#xff0c;在 regularexpression 中&#xff0c;一个 dot 代表匹配一个字元。
&#39;string&#39; 单引号 (single quote)
被单引号用括住的内容&#xff0c;将被视为单一字串。在引号内的代表变数的$符号&#xff0c;没有作用&#xff0c;也就是说&#xff0c;他被视为一般符号处理&#xff0c;防止任何变量替换。
heyyou&#61;homeecho &#39;$heyyou&#39; # We get $heyyou
\"string\" 双引号 (double quote)
被双引号用括住的内容&#xff0c;将被视为单一字串。它防止通配符扩展&#xff0c;但允许变量扩展。这点与单引数的处理方式不同。
heyyou&#61;homeecho \"$heyyou\" # We get home
&#96;command&#96; 倒引号 (backticks)
在前面的单双引号&#xff0c;括住的是字串&#xff0c;但如果该字串是一列命令列&#xff0c;会怎样&#xff1f;答案是不会执行。要处理这种情况&#xff0c;我们得用倒单引号来做。
fdv&#61;&#96;date &#43;%F&#96;echo \"Today $fdv\"
在倒引号内的 date &#43;%F 会被视为指令&#xff0c;执行的结果会带入 fdv 变数中。
, 逗点 (comma&#xff0c;标点中的逗号)
这个符号常运用在运算当中当做\"区隔\"用途。如下例
#!/bin/bashlet \"t1 &#61; ((a &#61; 5 &#43; 3, b &#61; 7 - 1, c &#61; 15 / 3))\"echo \"t1&#61; $t1, a &#61; $a, b &#61; $b\"
/ 斜线 (forward slash)
在路径表示时&#xff0c;代表目录。
cd /etc/rc.dcd ../..cd /
通常单一的 / 代表 root 根目录的意思&#xff1b;在四则运算中&#xff0c;代表除法的符号。
let \"num1 &#61; ((a &#61; 10 / 2, b &#61; 25 / 5))\"
\\ 倒斜线
在交互模式下的escape 字元&#xff0c;有几个作用&#xff1b;放在指令前&#xff0c;有取消 aliases的作用&#xff1b;放在特殊符号前&#xff0c;则该特殊符号的作用消失&#xff1b;放在指令的最末端&#xff0c;表示指令连接下一行。
# type rmrm is aliased to &#96;rm -i&#39;# \\rm ./*.log
上例&#xff0c;我在 rm 指令前加上 escape 字元&#xff0c;作用是暂时取消别名的功能&#xff0c;将 rm 指令还原。
# bkdir&#61;/home# echo \"Backup dir, \\$bkdir &#61; $bkdir\"Backup dir,$bkdir &#61; /home
上例 echo 内的 \\$bkdir&#xff0c;escape 将 $ 变数的功能取消了&#xff0c;因此&#xff0c;会输出 $bkdir&#xff0c;而第二个 $bkdir则会输出变数的内容 /home。
| 管道 (pipeline)
pipeline 是 UNIX 系统&#xff0c;基础且重要的观念。连结上个指令的标准输出&#xff0c;做为下个指令的标准输入。
who | wc -l
善用这个观念&#xff0c;对精简 script 有相当的帮助。
! 惊叹号(negate or reverse)
通常它代表反逻辑的作用&#xff0c;譬如条件侦测中&#xff0c;用 !&#61; 来代表\"不等于\"
if [ \"$?\" !&#61; 0 ]thenecho \"Executes error\"exit 1fi
在规则表达式中她担任 \"反逻辑\" 的角色
ls a[!0-9]
上例&#xff0c;代表显示除了a0, a1 .... a9 这几个文件的其他文件。
: 冒号
在 bash 中&#xff0c;这是一个内建指令&#xff1a;\"什么事都不干\"&#xff0c;但返回状态值 0。
:
echo $? # 回应为 0
: > f.$$
上面这一行&#xff0c;相当于 cat /dev/null >f.$$。不仅写法简短了&#xff0c;而且执行效率也好上许多。
有时&#xff0c;也会出现以下这类的用法
: ${HOSTNAME?} ${USER?} ${MAIL?}
这行的作用是&#xff0c;检查这些环境变数是否已设置&#xff0c;没有设置的将会以标准错误显示错误讯息。像这种检查如果使用类似 test 或 if这类的做法&#xff0c;基本上也可以处理&#xff0c;但都比不上上例的简洁与效率。
? 问号 (wild card)
在文件名扩展(Filename expansion)上扮演的角色是匹配一个任意的字元&#xff0c;但不包含 null 字元。
# ls a?a1
善用她的特点&#xff0c;可以做比较精确的档名匹配。
* 星号 (wild card)
相当常用的符号。在文件名扩展(Filename expansion)上&#xff0c;她用来代表任何字元&#xff0c;包含 null 字元。
# ls a*a a1 access_log
在运算时&#xff0c;它则代表 \"乘法\"。
let \"fmult&#61;2*3\"
除了内建指令 let&#xff0c;还有一个关于运算的指令expr&#xff0c;星号在这里也担任\"乘法\"的角色。不过在使用上得小心&#xff0c;他的前面必须加上escape 字元。
** 次方运算
两个星号在运算时代表 \"次方\" 的意思。
let \"sus&#61;2**3\"echo \"sus &#61; $sus\" # sus &#61; 8
$ 钱号(dollar sign)
变量替换(Variable Substitution)的代表符号。
vrs&#61;123echo \"vrs &#61; $vrs\" # vrs &#61; 123
另外&#xff0c;在 Regular Expressions 里被定义为 \"行\" 的最末端 (end-of-line)。这个常用在grep、sed、awk 以及 vim(vi) 当中。
${} 变量的正规表达式
bash 对 ${} 定义了不少用法。以下是取自线上说明的表列
${parameter:-word} ${parameter:&#61;word} ${parameter:?word} ${parameter:&#43;word} ${parameterffset} ${parameterffset:length} ${!prefix*} ${#parameter} ${parameter#word} ${parameter##word} ${parameter%word} ${parameter%%word} ${parameter/pattern/string} ${parameter//pattern/string}
$*
$* 引用script的执行引用变量&#xff0c;引用参数的算法与一般指令相同&#xff0c;指令本身为0&#xff0c;其后为1&#xff0c;然后依此类推。引用变量的代表方式如下&#xff1a;
$0, $1, $2, $3, $4, $5, $6, $7, $8, $9, ${10}, ${11}.....
个位数的&#xff0c;可直接使用数字&#xff0c;但两位数以上&#xff0c;则必须使用 {} 符号来括住。
$* 则是代表所有引用变量的符号。使用时&#xff0c;得视情况加上双引号。
echo \"$*\"
还有一个与 $* 具有相同作用的符号&#xff0c;但效用与处理方式略为不同的符号。
$&#64;
$&#64; 与 $* 具有相同作用的符号&#xff0c;不过她们两者有一个不同点。
符号 $* 将所有的引用变量视为一个整体。但符号 $&#64; 则仍旧保留每个引用变量的区段观念。
$#
这也是与引用变量相关的符号&#xff0c;她的作用是告诉你&#xff0c;引用变量的总数量是多少。
echo \"$#\"
$? 状态值 (status variable)
一般来说&#xff0c;UNIX(linux) 系统的进程以执行系统调用exit()来结束的。这个回传值就是status值。回传给父进程&#xff0c;用来检查子进程的执行状态。
一般指令程序倘若执行成功&#xff0c;其回传值为 0&#xff1b;失败为 1。
tar cvfz dfbackup.tar.gz /home/user > /dev/nullecho\"$?\"$$
由于进程的ID是唯一的&#xff0c;所以在同一个时间&#xff0c;不可能有重复性的 PID。有时&#xff0c;script会需要产生临时文件&#xff0c;用来存放必要的资料。而此script亦有可能在同一时间被使用者们使用。在这种情况下&#xff0c;固定文件名在写法上就显的不可靠。唯有产生动态文件名&#xff0c;才能符合需要。符号$$或许可以符合这种需求。它代表当前shell 的 PID。
echo \"$HOSTNAME, $USER, $MAIL\" > ftmp.$$
使用它来作为文件名的一部份&#xff0c;可以避免在同一时间&#xff0c;产生相同文件名的覆盖现象。
ps: 基本上&#xff0c;系统会回收执行完毕的 PID&#xff0c;然后再次依需要分配使用。所以 script 即使临时文件是使用动态档名的写法&#xff0c;如果script 执行完毕后仍不加以清除&#xff0c;会产生其他问题。
( ) 指令群组 (command group)
用括号将一串连续指令括起来&#xff0c;这种用法对 shell 来说&#xff0c;称为指令群组。如下面的例子&#xff1a;(cd ~ ; vcgh&#61;&#96;pwd&#96; ;echo $vcgh)&#xff0c;指令群组有一个特性&#xff0c;shell会以产生 subshell来执行这组指令。因此&#xff0c;在其中所定义的变数&#xff0c;仅作用于指令群组本身。我们来看个例子
# cat ftmp-01#!/bin/basha&#61;fsh(a&#61;incg ; echo -e \"\\n $a \\n\")echo $a#./ftmp-01incgfsh
除了上述的指令群组&#xff0c;括号也用在 array 变数的定义上&#xff1b;另外也应用在其他可能需要加上escape字元才能使用的场合&#xff0c;如运算式。
(( ))
这组符号的作用与 let 指令相似&#xff0c;用在算数运算上&#xff0c;是 bash 的内建功能。所以&#xff0c;在执行效率上会比使用 let指令要好许多。
#!/bin/bash(( a &#61; 10 ))echo -e \"inital value, a &#61; $a\\n\"(( a&#43;&#43;))echo \"after a&#43;&#43;, a &#61; $a\"
{ } 大括号 (Block of code)
有时候 script 当中会出现&#xff0c;大括号中会夹着一段或几段以\"分号\"做结尾的指令或变数设定。
# cat ftmp-02#!/bin/basha&#61;fsh{a&#61;inbc ; echo -e \"\\n $a \\n\"}echo $a#./ftmp-02inbcinbc
这种用法与上面介绍的指令群组非常相似&#xff0c;但有个不同点&#xff0c;它在当前的 shell 执行&#xff0c;不会产生 subshell。
大括号也被运用在 \"函数\" 的功能上。广义地说&#xff0c;单纯只使用大括号时&#xff0c;作用就像是个没有指定名称的函数一般。因此&#xff0c;这样写 script也是相当好的一件事。尤其对输出输入的重导向上&#xff0c;这个做法可精简 script 的复杂度。
此外&#xff0c;大括号还有另一种用法&#xff0c;如下
{xx,yy,zz,...}
这种大括号的组合&#xff0c;常用在字串的组合上&#xff0c;来看个例子
mkdir {userA,userB,userC}-{home,bin,data}
我们得到 userA-home, userA-bin, userA-data, userB-home, userB-bin,userB-data, userC-home, userC-bin,userC-data&#xff0c;这几个目录。这组符号在适用性上相当广泛。能加以善用的话&#xff0c;回报是精简与效率。像下面的例子
chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}