在 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