在 bash 中,如果一个字符不仅具有其字面含义,而且还具有元语义(meta-meaning),那么就可以称该字符为 特殊字符
。特殊字符如同命令、关键字一样,是 bash 脚本的重要组成部分。这篇文章会对 bash 脚本编程中的特殊字符进行总结。
注释符 #
#
是 bash 里的注释符,如果脚本行以 # 开头,那么该行就被认为是注释行,不会被执行。bash 本身没有多行注释的语法,相反是通过 多个单行注释
来编写一段注释内容。
1 2 3 4 5 6 7 8 9 10 #!/bin/bash echo "comment 1" echo "comment 2"
注释也可以在命令行的结尾,但此时要注意命令结尾和 #
字符之间需要存在空格:
1 2 3 4 5 this follow comment this not follow comment
脚本中并不是所有的 #
都会被认为是注释的开始,例如 echo 语句中被引用或转移的 #
字符就没有特殊含义,一些模式匹配操作也使用 #
字符:
命令分隔符 ;
可以在一行编写多条命令,这些命令之间使用 ;
进行分隔。
在同一行的 if then
之间也要使用使用 ;
分隔。在一行中编写复杂 if-elif-else
语句的语法为:
1 if list; then list; [ elif list; then list; ] ... [ else list; ] fi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #!/bin/bash if [ true ]then echo "true" else echo "false" fi if [ true ]; then echo "true" else echo "false" fi if [ true ]; then echo "true" ; else echo "false" ; fi t=2 if [ $t -eq 0 ]then echo "0" elif [ $t -eq 1 ]then echo "1" else echo "2" fi if [ $t -eq 0 ]; then echo "0" ; elif [ $t -eq 1 ]; then echo "1" ; else echo "2" ; fi
case 语句终止符 ;;
1 2 3 4 5 6 7 8 #!/bin/bash t=5 case "$t " in 1) echo "1" ;; 2) echo "2" ;; *) echo "any" ;; esac
另外,bash4 中引入了 ;&
和 ;;&
语法,用于增加 case 匹配的灵活性。
;;
匹配某个分支并执行对应语句后,不会继续匹配
;&
匹配某个分支并执行对应语句后,会继续执行下一个匹配分支的语句
;;&
匹配某个分支并执行对应语句后,会继续尝试匹配下一个分支
点字符 .
bash 中,点字符 .
的含义较多,下面将逐一介绍。
等同于 source
命令,. filename
或者 source filename
都可以在当前 shell 环境中执行指定的脚本文件
当作为目录路径时,.
表示当前目录,..
表示上层目录
双引号 "
与单引号 '
用双引号来进行部分引用(大部分特殊字符都失去其特殊含义)
用单引号来进行全引用(全部特殊字符都失去其特殊含义)
部分引用
与 全引用
的详细区别将在后续文章介绍。
逗号符 ,
逗号符 ,
可以串联一系列算术表达式,此时算术表达式依次执行,但是只返回最后一个算术表达式的结果。此时这个逗号符的功能类似于 C
语言中的逗号运算符。
在 花括号扩展
中,逗号符也可以作为可选字符串的分隔符:
1 2 /bin /usr/bin /usr/local/bin
在 Bash4 中,引入了通过 ,,
、,
来在参数替换中实现将 大写字符转换为小写形式
,;;
实现的最长匹配,,
则是最短匹配。
1 2 3 4 echo "${v,} " echo "${v,,} "
转义字符 \
\
转义字符可以对特殊字符进行转义,使其失去其特殊含义,而表示其字面含义。
/
字符/
字符主要有两个含义:
/
作为路径分隔符使用
在算术运算中表示 除法运算
反引号 `` 字符 反引号 command
字符被称为命令替换符,它可以获取反引号内 command 命令的执行结果,之后可以将该结果赋值给变量。
冒号字符 :
:
字符在 shell 中表示 NOP
空操作,它与 shell 内建命令 true 有相同的效果。它本身也是 bash 的内建命令之一,返回值是 true(0)。
1 while :; do echo "loop" ; done
1 if true ; then :; else echo "false" ; fi
1 2 3 4 5 6 7 Command 'root' not found, did you mean: ...... root
与 >
一起使用,在不改变文件权限的情况下清空文件,如果文件不存在将创建一个文件
:
也可以作为域分隔符,例如在 $PATH
、/etc/password
中使用
取反操作符 !
取反操作符 !
可以对命令返回的状态码进行取反。
*
字符
当作为文件通配符使用时,*
可以匹配任意字符串
当在正则表达式中使用时,*
可以匹配任意多个(包含 0 个)前一个字符
在算术运算中,*
作为乘法使用,**
作为乘方使用
?
字符
当作为文件通配符使用时,?
可以匹配任意单个字符
在参数替换表达式中,?
用来测试一个变量是否已经赋值
在正则表达式中使用时,?
可以匹配 0-1
个前一个字符
在 (())
中,可以使用类似于 C 语言的三元操作符 expr?expr:expr
(注意中间没有空格)
1 2 3 4 -bash: p: parameter not set
$
字符$
字符也有多个作用:
$
最主要的作用是进行变量替换,即取出变量的内容
${}
进行参数替换
$'...'
引用字符串扩展,这种方式可以将转义八进制或十六进制的值转换成 ASCII 或者 Unicode 字符
$*
、$@
都是位置参数。
$?
退出状态变量,或者称为退出码。保存命令、函数或脚本自身的退出状态
$$
进程ID变量,保存运行脚本的当前进程 ID
在正则表达式中,$
匹配行结尾
()
符号(comamnd1; command2; ...)
可以认为起到命令组的作用,()
会产生一个子 shell 来执行括号中的一些列命令。由于父进程脚本不能访问子进程中所创建的变量,因此在子 shell 中定义的变量,在脚本的其他部分是不可见的。
()
也用于 bash 数组初始化:
1 Array=(element1 element2 element3)
{}
符号bash 中,花括号的作用也非常多,下面逐一介绍。
首先是花括号扩展结构,此时用花括号包裹一系列选项,并在选项之间用逗号分隔开,bash 会将花括号中的选项依次展开,然后执行命令。注意此时花括号内不能有空格(除非进行转义):
1 2 3 4 "These" "words" "are" "quoted"
bash 的第三版引入了 {a..z}
、{0..100}
等可扩展 花括号扩展语法
:
1 2 3 4 5 6 a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
{}
可以创建代码块,又被称为内联组,它实际上创建了一个匿名函数。不同于标准的函数,代码块内的变量在脚本的其他部分依然是可见的。
1 2 3 4 -bash: local : can only be used in a function 132
代码块可以经由 I/O 重定向进行输入或者输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #!/bin/bash File=/etc/fstab { read line1 read line2 } < $File echo "First line in $File is:" echo $line1 echo "Second line in $File is:" echo $line2 exit 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #!/bin/bash E_SUCCESS=0 E_NOARGS=65 if [ -z "$1 " ]; then echo "Usage: `basename $0 ` rpm-file" exit $E_NOARGS fi { echo echo "Archive Description" rpm -qpi $1 echo echo echo "Archive Listring" rpm -qpl $1 echo rpm -i --test $1 if [ $? -eq $E_SUCCESS ]; then echo "$1 can be installed" else echo "$1 cannot be installed" fi echo } > "$1 .test" echo "Result of RPM test in file $1 .test" exit $E_SUCCESS
注意,与 ()
中的命令组不同的是,{}
中的代码块不会在一个新的 shell 子进程中运行。
{}
其他的作用包括:
用于 for 循环语句
文本占位符,在 xargs -i
中 -i
选项等效于 -I {}
,即将 {}
作为本文占位符
1 ls . | xargs -i -t cp ./{} $1
[]
符号[]
符号也有多种多种用途。首先是作为测试符号,[
是 shell 内建测试命令。
在数组上下文中,[xxx]
表示用特定的偏移量 xxx
访问数组中元素:
[]
也可以在正则表达式中表示字符范围
$[...]
表示整数扩展符,可以在 $[]
中计算整数的算术表达式。
另外,[[]]
也key已进行测试,相比于 []
,它更加灵活。[[]]
是 shell 的关键字。
(())
符号可以在 (())
中实现整数的运算:
重定向
scriptname >filename
将脚本 scriptname
的标准输出重定向到文件 filename
中
scriptname &>filename
将脚本 scriptname
的标准输出和标准错误重定向到文件 filename
中。该方式与以下两种方式效果等价
scriptname >&filename
scriptname >filename 2>&1
scriptname >&2
将脚本 scriptname
的标准输出重定向到标准错误
scriptname >>filename
将脚本 scriptname
的标准输出以追加的方式添加到 filename
文件末尾
scriptname &>>filename
将脚本 scriptname
的标准输出和标准错误以追加的方式添加到 filename
文件末尾
<<
可以在 here document
中实现重定向
>>
可以在 here string
中实现重定向
重定向在用于清除测试条件的输出时特别有用,例如测试一个命令是否存在:
在某些上下文中,<
>
可以实现字符串或数字的比较。
另外,<>
可以在正则表达式中表示单词的边界:
管道符 |
管道可以将上一个命令的输出作为下一个命令的输入,管道是一种可以将一系列命令连接起来的绝佳方式。
命令的输出同样可以通过管道输入到脚本中:
1 2 3 4 #!/bin/bash tr a-z A-Z
1 2 3 4 ls -l | bash upper.shTOTAL 40 -RW-R--R-- 1 ROOT ROOT 169 MAY 19 16:20 BRACE_IN.SH -RW-R--R-- 1 ROOT ROOT 484 MAY 19 16:27 BRACE_OUT.SH
在管道中,每个进程的输出必须作为下一个进程的输入被正确读入,否则数据流会被阻塞,管道就无法按照预期工作。而且需要注意,**管道是在一个子进程中运行的,因此它并不能修改父进程脚本中的变量。
如果管道中的任意一个命令意外中止了,管道将会提前中断,称之为 管道破裂
。出现这种情况,系统将发送 SIGPIPE 信号。
||
||
实现逻辑或操作,即测试结构中,任意一个测试条件为真,整个表达式为真,返回 0。
&&
&&
实现逻辑与操作,即测试结构中,所有测试条件为真,整个表达式才为真,返回 0。
&
符号&
表示后台操作符,如果命令后带有 &
,那么该命令将会在后台运行。
在脚本中,命令甚至循环都可以在后台运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #!/bin/bash for i in `seq 1 10`do echo -n "$i " done &echo for i in `seq 11 20`do echo -n "$i " done echo
-
符号-
符号在 bash 中也有多种用途:
选项前缀,在使用命令时,如果需要使用选项参数,选项前面要加 -
。另外,--
则通常表示命令的长选项前缀
例如下面的 cat -
表示从标准输入中进行读取
如下命令将整个文件树从一个目录移动到另一个目录中:这里 tar cf - .
表示输出到标准输出中,这是一种在管道中使用面向文件工具作为过滤器的方法:
1 (cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
在 cd 命令,cd -
表示返回先前的工作目录
-
也可以用于减号运算符
=
符号
=
可以作为赋值操作符
在某些情况下,也可以作为字符串比较操作符
+
符号
+
可以作为加号运算符
在正则表达式中,+
表示匹配一个或多个 前一个字符
一些特定的指令和内建命令使用 +
启用特定的选项,使用 -
禁用特定的选项
在参数替换中,${parameter:+word}
用来使用可选值。即如果 parameter
为空或者未设置,则不进行替换,否则会使用 word 的扩展
~
符号
~
可以表示当前用户的主目录,相当于 $HOME
变量,~xxx
则表示用户 xxx
的主目录
~+
表示当前工作目录,相当于 $PWD
变量
~-
表示之前的工作目录,相当于 $OLDPWD
变量
~
也可以作为正则表达式中的行首匹配符
^
符号
^
在正则表达式中匹配行首
^
以及 ^^
在参数替换中,可以实现大写转换。^^
实现了最长匹配,^
则是最短匹配
控制字符 控制字符可以改变终端或文件显示的一些行为。这篇文章 详细总结了 bash 的一些控制字符。
空白符 空白符包含空格、制表符、换行符或它们的任意组合。在某些场景,比如变量赋值时,空白符不应该出现,否则会造成语法错误。特殊变量 $IFS
可以作为一些特定命令的输入域(field)分隔符,默认值为空白符。如果想要在字符串或者变量中保留空白符,需要引用。
UNIX 可以使用 POSIX 字符类 [:space:]
来寻找和操作空白符。
Reference