3. 特殊字符
是什么让一个字符变得特殊呢?如果一个字符不仅具有字面意义,而且具有元意(meta-meaning),我们就称它为特殊字符。特殊字符同命令和关键词(keywords)一样,是bash脚本的组成部分。
你在脚本或其他地方都能够找到特殊字符。
# ###
注释符。如果一行脚本的开头是#(除了#!),那么代表这一行是注释,不会被执行。
# 这是一行注释注释也可能会在一行命令结束之后出现。
echo "A comment will follow." # 这儿可以写注释
# ^ 注意在#之前有空格注释也可以出现在一行开头的空白符(whitespace)之后。
# 这个注释前面存在着一个制表符(tab)注释甚至可以嵌入到管道命令(pipe)之中。
initial=( `cat "$startfile" | sed -e '/#/d' | tr -d '\n' |\
# 删除所有带'#'注释符号的行
sed -e 's/\./\. /g' -e 's/_/_ /g'` )
# 摘录自脚本 life.sh
命令不能写在同一行注释之后。因为没有任何方法可以结束注释(仅支持单行注释),为了让新命令正常执行,另起一行写吧。
当然,在
echo语句中被引用或被转义的#不会被认为是注释。同样,在某些参数替换式或常量表达式中的#也不会被认为是注释。
echo "The # here does not begin a comment."
echo 'The # here does not begin a comment.'
echo The \# here does not begin a comment.
echo The # here begins a comment.
echo ${PATH#*:} # 参数替换而非注释
echo $(( 2#101011 )) # 进制转换而非注释
# 感谢S.C.因为引用符和转义符(" ' \)转义了#。
一些模式匹配操作同样使用了#。
;
命令分隔符[分号]。允许在同一行内放置两条或更多的命令。
注意有时候";"需要被转义才能正常工作。
;;
case条件语句终止符[双分号]。
;;&, ;&
case条件语句终止符(Bash4+ 版本)。
.
句点命令[句点]。等价于source命令(查看样例 15-22)。这是一个bash的内建命令。
.
句点可以作为文件名的一部分。如果它在文件名开头,那说明此文件是隐藏文件。使用不带参数的ls命令不会显示隐藏文件。
当句点出现在目录中时,单个句点代表当前工作目录,两个句点代表上级目录。
句点通常代表文件移动的目的地(目录),下式代表的是当前目录。
复制所有的“垃圾文件”到
当前目录
.
句点匹配符。在正则表达式中,点号意味着匹配任意单个字符。
"
部分引用[双引号]。在字符串中保留大部分特殊字符。详细内容将在第五章介绍。
'
全引用[单引号]。在字符串中保留所有的特殊字符。是部分引用的强化版。详细内容将在第五章介绍。
,
逗号运算符。逗号运算符将一系列的算术表达式串联在一起。算术表达式依次被执行,但只返回最后一个表达式的值。
逗号运算符也可以用来连接字符串。
,, ,
在参数替换中进行小写字母转换(Bash4 新增)。
\
转义符[反斜杠]。转义某字符的标志。
\x转义了字符x。双引号""内的X与单引号内的X具有同样的效果。 转义符也可以用来转义"与',使它们表达其字面含义。
第五章将更加深入的解释转义字符。
/
文件路径分隔符[正斜杠]。起分割路径的作用。(比如 /home/bozo/projects/Makefile)
它也在算术运算中充当除法运算符。
`
命令替换符。`command` 结构可以使得命令的输出结果赋值给一个变量。通常也被称作后引号(backquotes)或反引号(backticks)。
:
空命令[冒号]。它在shell中等价于"NOP"(即no op,空操作)与shell内建命令true有同样的效果。它本身也是Bash的内建命令之一,返回值是true(0)。
在无限循环中的应用:
可在 if/then 中充当占位符:
在二元操作中作占位符: 查看样例 8-2或默认参数部分。
查看样例 19-10了解空命令在here document中作为占位符的情况。
使用参数替换为字符串变量赋值(查看样例 10-7)。
查看变量扩展或字符串替换章节了解空命令在其中的作用。
与>重定向操作符结合,可以在不改变文件权限的情况下清空文件。如果文件不存在,那么将创建这个文件。
也可查看样例 16-15。
与>>重定向操作符结合,将不会清空任何已存在的文件(: >> target_file)。如果文件不存在,将创建这个文件。
以上操作仅适用于普通文件,不适用于管道、符号链接和特殊文件。
空命令可以用来作为一行注释的开头,尽管我们并不推荐这么做。使用 # 可以使解释器关闭该行的错误检测,所以几乎所有的内容都可以出现在注释#中。使用空命令却不是这样的:
:也可以作为一个域分隔符,比如在/etc/passwd和 $PATH 变量中。
将冒号作为函数名也是可以的。
这种写法并不具有可移植性,也不推荐使用。事实上,在Bash的最近的版本更新中已经禁用了这种用法。但我们还可以使用下划线 _来替代。
冒号也可以作为非空函数的占位符。
!
取反(或否定)操作符[感叹号]。! 操作符反转已执行的命令的返回状态(查看样例 6-2)。它同时可以反转测试操作符的意义,例如可以将相等(=)反转成不等(!=)。它是一个Bash关键词。
在一些特殊场景下,它也会出现在间接变量引用中。
在另外一些特殊场景下,即在命令行下可以使用 ! 调用Bash的历史记录(附录 L)。需要注意的是,在脚本中,这个机制是被禁用的。
*
通配符[星号]。在文件匹配(globbing)操作时扩展文件名。如果它独立出现,则匹配该目录下的所有文件。
在正则表达式中表示匹配任意多个(包括0)前个字符。
*
算术运算符。在进行算术运算时,表示乘法运算。
** 双星号可以表示乘方运算或扩展文件匹配。
?
测试操作符[问号]。在一些特定的语句中,? 表示一个条件测试。
在一个双圆括号结构中,? 可以表示一个类似C语言风格的三元(trinary)运算符的一个组成部分。
condition?result-if-true:result-if-false
在参数替换表达式中,? 用来测试一个变量是否已经被赋值。
?
通配符。它在进行文件匹配(globbing)时以单字符通配符扩展文件名。 在扩展正则表达式中匹配一个单字符。
$
取值符号[钱字符],用来进行变量替换(即取出变量的内容)。
如果在变量名前有 $,则表示此变量的值。
$
行结束符[EOF]。 在正则表达式中,$ 匹配行尾字符串。
${}
参数替换。
$'...'
引用字符串扩展。这个结构将转义八进制或十六进制的值转换成ASCII或Unicode字符。
$*, $@ ###
位置参数。
$?
返回状态变量。此变量保存一个命令、一个函数或该脚本自身的返回状态。
$$
进程ID变量。此变量保存该运行脚本的进程ID。
()
命令组。
(a=hello; echo $a)
通过括号执行一系列命令会产生一个子shell(subshell)。 括号中的变量,即在子shell中的变量,在脚本的其他部分是不可见的。父进程脚本不能访问子进程(子shell)所创建的变量。
数组初始化。
{xxx,yyy,zzz,...}
花括号扩展结构。
这个命令可以作用于花括号内由逗号分隔的文件描述列表。 文件名扩展(匹配)作用于大括号间的各个文件。
除非被引用或被转义,否则空白符不应在花括号中出现。
{a..z}
扩展的花括号扩展结构。
Bash第三版中引入了 {a..z} 扩展的花括号扩展结构。
{}
代码块[花括号],又被称作内联组(inline group)。它实际上创建了一个匿名函数(anonymous function),即没有名字的函数。但是,不同于那些“标准”函数,代码块内的变量在脚本的其他部分仍旧是可见的。
代码块可以经由I/O重定向进行输入或输出。
样例 3-1. 代码块及I/O重定向
样例 3-2. 将代码块的输出保存至文件中
与由圆括号包裹起来的命令组不同,由花括号包裹起来的代码块不产生子进程。
也可以使用非标准的 for 循环语句来遍历代码块。
{}
文本占位符。在 xargs -i 后作为输出的占位符使用。
{} \;
路径名。通常在 find 命令中使用,但这不是shell的内建命令。
定义:路径名是包含完整路径的文件名,例如
/home/bozo/Notes/Thursday/schedule.txt。我们通常又称之为绝对路径。
在执行find -exec时最后需要加上;,但是分号需要被转义以保证其不会被shell解释。
[ ]
测试。在 [ ] 之间填写测试表达式。值得注意的是,[ 是shell内建命令 test 的一个组成部分,而不是外部命令 /usr/bin/test 的链接。
[[ ]]
测试。在 [[ ]] 之间填写测试表达式。相比起单括号测试 ([ ]),它更加的灵活。它是一个shell的关键字。
详情查看关于 [[ ]] 结构的讨论。
[ ]
数组元素。在数组中,可以使用中括号的偏移量来用来访问数组中的每一个元素。
[ ]
字符集、字符范围。 在正则表达式中,中括号用来匹配指定字符集或字符范围内的任意字符。
$[ ... ]
整数扩展符。在 $[ ] 中可以计算整数的算术表达式。
(( ))
整数扩展符。在 (( )) 中可以计算整数的算术表达式。
详情查看关于 (( ... )) 结构的讨论。
> &> >& >> < <>
重定向。
scriptname >filename 将脚本 scriptname 的输出重定向到 filename 中。如果文件存在,那么覆盖掉文件内容。
command &>filename 将命令 command 的标准输出(stdout) 和标准错误输出(stderr) 重定向到 filename。
重定向在用于清除测试条件的输出时特别有效。例如测试一个特定的命令是否存在。
或写在脚本中:
command >&2 将命令的标准输出重定向至标准错误输出。
scriptname >>filename 将脚本 scriptname 的输出追加到 filename 文件末尾。如果文件不存在,那么将创建这个文件。
[i]<>filename 打开文件 filename 用来读写,并且分配一个文件描述符i指向它。如果文件不存在,那么将创建这个文件。
进程替换: (command)> <(command)
在某些情况下, "<" 与 ">" 将用作字符串比较。
在另外一些情况下, "<" 与 ">" 将用作数字比较。详情查看样例 16-9。
<<
在here document中进行重定向。
<<<
在here string中进行重定向。
<, >
ASCII码比较。
\<, >
正则表达式中的单词边界(word boundary)。
|
管道(pipe)。管道可以将上一个命令的输出作为下一个命令的输入,或者直接输出到shell中。管道是一种可以将一系列命令连接在一起的绝妙方式。
管道是一种在进程间通信的典型方法。它将一个进程的输出作为另一个进程的输入。举一个经典的例子,像
cat或者echo这样的命令,可以通过管道将它们产生的数据流导入到过滤器(filter)中。过滤器是可以用来处理输入流的命令。
cat $filename1 $filename2 | grep $search_word查看UNIX FAQ第三章获取更多关于使用UNIX管道的信息。
命令的输出同样可以通过管道输入到脚本中。
现在,让我们将 ls -l 的输出通过管道导入到脚本中。
在管道中,每一个进程的输出必须作为下个进程的输入被正确读入,如果不这样,数据流会被阻塞(block),管道就无法按照预期正常工作。
管道是在一个子进程中运行的,因此它并不能修改父进程脚本中的变量。
如果管道中的任意一个命令意外中止了,管道将会提前中断,我们称其为管道破裂(Broken Pipe)。出现这种情况,系统将发送一个 SIGPIPE 信号。
>|
强制重定向。即使在 noclobber 选项被设置的情况下,重定向也会覆盖已存在的文件。
||
或(OR)逻辑运算符。在测试结构中,任意一个测试条件为真,整个表达式为真。返回 0(成功标志位)。
&
后台运行操作符。如果命令后带&,那么此命令将转至后台运行。
在脚本中,命令甚至循环都可以在后台运行。
样例 3-3. 在后台运行的循环
脚本在后台执行命令时可能因为等待键盘事件被挂起。幸运的是,有一套方案可以解决这个问题。
&&
与(AND)逻辑操作符。在测试结构中,所有测试条件都为真,表达式才为真,返回 0(成功标志位)。
-
选项与前缀。它可以作为命令的选项标志,也可以作为一个操作符的前缀,也可以作为在参数代换中作为默认参数的前缀。
COMMAND -[Option1][Option2][..]
ls -al
sort -dfu $filename
--
双横线一般作为命令长选项的前缀。
sort --ignore-leading-blanks
双横线与Bash内建命令一起使用时,意味着该命令选项的结束。
下面提供了一种删除文件名以横线开头文件的简单方法。
双横线通常也和 set 连用。
set -- $variable(查看样例 15-18)。
-
重定向输入输出[短横线]。
在这个例子中,cat - 输出由键盘读入的标准输入(stdin) 到 标准输出(stdout)。但是在真实应用的 I/O 重定向中是否有使用 '-'?
下面的例子中,"-" 并不是一个Bash的操作符,它仅仅是 tar, cat 等一些特定UNIX命令中将结果输出到标准输出的选项。
当需要文件名的时候,- 可以用来代替某个文件而重定向到标准输出(通常出现在 tar cf 中)或从 stdin 中接受数据。这是一种在管道中使用面向文件(file-oriented)工具作为过滤器的方法。
单独执行 file 命令,将会得到一条错误信息。
在命令后增加一个 "-" 可以得到一个更加有用的结果。它会使得shell暂停等待用户输入。
现在命令能够接受标准输入并且处理它们了。
"-" 能够通过管道将标准输出重定向到其他命令中。这就可以做到像在某个文件前添加几行这样的事情。
使用 diff 比较两个文件的部分内容:
最后介绍一个使用 - 的 tar 命令的实际案例。
样例 3-4. 备份最近一天修改过的所有文件
以 "-" 开头的文件在和"-" 重定向操作符一起使用时可能会导致一些问题。因此合格的脚本必须首先检查这种情况。如果遇到,就需要给文件名加一个合适的前缀,比如 ./-FILENAME, $PWD/-FILENAME 或者$PATHNAME/-FILENAME 。
如果变量的值以 '-' 开头,也可能会造成类似问题。
-
先前的工作目录。使用 cd - 命令可以返回先前的工作目录。它实际上是使用了 $OLDPWD 环境变量。
不要将这里的 "-" 与先前的 "-" 重定位操作符混淆。"-" 的具体含义需要根据上下文来解释。
-
减号。算术运算符中的减法标志。
=
等号。赋值操作符。
在一些情况下,"=" 可以作为字符串比较操作符。
+
加号。加法算术运算。
在一些情况下,+ 是作为正则表达式中的一个操作符。
+
选项操作符。作为一个命令或过滤器的选项标记。
特定的一些指令和内建命令使用 + 启用特定的选项,使用 - 禁用特定的选项。在参数代换中,+ 是作为变量扩展的备用值(alternate value)的前缀。
%
取模。取模操作运算符。
在另外一些情况下,% 是一种模式匹配的操作符。
~
主目录[波浪号]。它相当于内部变量 $HOME。~bozo 是 bozo 的主目录,执行 ls ~bozo 将会列出他的主目录中内容。~/ 是当前用户的主目录,执行 ls ~/ 将会列出其中所有的内容。
~+
当前工作目录。它等同于内部变量 $PWD。
~-
先前的工作目录。它等同于内部变量 $OLDPWD。
=~
正则表达式匹配。将在 Bash version 3 章节中介绍。
^
行起始符。在正则表达式中,"^" 代表一行文本的开始。
^, ^^
参数替换中的大写转换符(在Bash第4版新增)。
控制字符
改变终端或文件显示的一些行为。一个控制符是由 CONTRL + key 组成的(同时按下)。控制字符同样可以通过转义以八进制或十六进制的方式显示。
控制符不能在脚本中使用。
Ctrl-A
移动光标至行首。
Ctrl-B
非破坏性退格(即不删除字符)。
Ctrl-C
中断指令。终止当前运行的任务。
Ctrl-D
登出shell(类似 exit)
键入 EOF(end-of-file,文件终止标记),中断 stdin 的输入。
当你在终端或 xterm 窗口中输入字符时,Ctl-D 将会删除光标上的字符。当没有字符时,Crl-D 将会登出shell。在 xterm 中,将会关闭整个窗口。
Ctrl-E
移动光标至行末。
Ctrl-F
光标向前移动一个字符。
Ctrl-G
响铃BEL。在一些老式打字机终端上,将会响铃。而在 xterm 中,将会产生“哔”声。
Ctrl-H
抹除(破坏性退格)。退格删除前面的字符。
Ctrl-I
水平制表符。
Ctrl-J
另起一行(换行)。在脚本中,你也可使用八进制 '\012' 或者十六进制 '\x0a' 来表示。
Ctrl-K
垂直制表符。
当你在终端或 xterm 窗口中输入字符时,Ctrl-K 将会删除光标上及其后的所有字符。而在脚本中,Ctrl-K 的作用有些不同。具体查看下方 Lee Lee Maschmeyer 写的样例。
Ctrl-L
清屏、走纸。在终端中等同于 clear 命令。在打印时,Ctrl-L 将会使纸张移动到底部。
Ctrl-M
回车(CR)。
Ctrl-N
在命令行历史记录中调用下一条历史命令。
Ctrl-O
在命令行中另起一行。
Ctrl-P
在命令行历史记录中调用上一条历史命令。
Ctrl-Q
恢复(XON)。
终端恢复读入 stdin。
Ctrl-R
在命令行历史记录中进行搜索。
Ctrl-S
挂起(XOFF)。
终端冻结 stdin。(可以使用 Ctrl-Q 恢复)
Ctrl-T
交换光标所在字符与其前一个字符。
Ctrl-U
删除光标所在字符之前的所有字符。 在一些情况下,不管光标在哪个位置,Ctrl-U 都会删除整行文字。
Ctrl-V
输入时,使用 Ctrl-V 允许插入控制字符。例如,下面两条语句是等价的:
Ctrl-V 在文本编辑器中特别有用。
Ctrl-W
当你在终端或 xterm 窗口中输入字符时,Ctrl-W 将会删除光标所在字符之前到其最近的空白符之间的所有字符。 在一些情况下,Ctrl-W 会删除到之前最近的非字母或数字的字符。
Ctrl-X
在一些特定的文本处理程序中,剪切高亮文本并复制到剪贴板(clipboard)。
Ctrl-Y
粘贴之前使用 Ctrl-U 或 Ctrl-W 删除的文字。
Ctrl-Z
暂停当前运行的任务。
在一些特定的文本处理程序中是替代操作。
在 MSDOS 文件系统中作为 EOF(end-of-file,文件终止标记)。
空白符
作为命令或变量之间的分隔符。空白符包含空格、制表符、换行符或它们的任意组合。在一些地方,比如变量赋值时,空白符不应该出现,否则会造成语法错误。
空白行在脚本中不会有任何实际作用,但是可以划分代码,使代码更具可读性。
特殊变量 $IFS 是作为一些特定命令的输入域(field)分隔符,默认值为空白符。
定义:域是字符串中离散的数据块。使用空白符或者指定的字符(通常由
$IFS决定)来分隔临近域。在一些情况下,域也可以被称作记录(record)。
如果想在字符串或者变量中保留空白符,请引用。
UNIX 过滤器可以使用 POSIX 字符类 [:space:] 来寻找和操作空白符。
Last updated
Was this helpful?