第三章:特殊字符

在脚本或其他别的地方出现的特殊字符

#

注释. 以一个#开头的行 (#!是例外) 是注释行.

   1 # 这是一行注释.

注释也可以出现在一个命令语句的后面。

   1 echo "A comment will follow." # 这里可以添加注释.
   2 #                            ^ 注意在#前面可以有空白符 #

注释行前面也可以有空白字符.

   1 	#注意这个注释行的开头是一个TAB键.

在同一行中,命令不会跟在一个注释的后面。因为这种情况下没有办法分辨注释的结尾,命令只能放在同一行的行首。用另外的一个新行开始下一个注释。

当然了,在echo命令给出的一个转义的#字符并不会开始一个注释。同样地,出现在一些参数代换结构和在数值常量表达式中的#字符也同样不会开始一个注释。
   1 echo "The # here does not begin a comment."
   2 echo 'The # here does not begin a comment.'
   3 echo The \# here does not begin a comment.
   4 echo The # here begins a comment.
   5 
   6 echo ${PATH#*:}       # 前面的#是参数代换,不是注释.
   7 echo $(( 2#101011 ))  # 基本转换,不是注释.
   8 
   9 # 多谢, S.C.
标准的引用和转义 符("'\)可以转义#。

当然,模式匹配操作也可以使用#,而不必把它当做注释的开始。

;

命令分割符[分号]. 分割符允许在同一行里有两个或更多的命令.

   1 echo hello; echo there
   2 
   3 
   4 if [ -x "$filename" ]; then    # 注意:"if" and "then"需要分隔符
   5                                # 思考一下这是为什么?
   6   echo "File $filename exists."; cp $filename $filename.bak
   7 else
   8   echo "File $filename not found."; touch $filename
   9 fi; echo "File test complete."

注意”;”有时需要转义.

;;

case语句分支的结束符[双分号].

   1 case "$variable" in
   2 abc)  echo "\$variable = abc" ;;
   3 xyz)  echo "\$variable = xyz" ;;
   4 esac

.

"点"命令[圆点]. 等同于source (参考例子 11-20).这是一个bash的内建命令.

.

"点", 作为一个文件名的组成部分.当点(.)以一个文件名为前缀时,起作用使该文件变成了隐藏文件。这种隐藏文件ls一般是不会显示出来的。[译者注:除非你加了选项-a]
 bash$ touch .hidden-file
 bash$ ls -l	      
 total 10
 -rw-r--r--    1 bozo      4034 Jul 18 22:04 data1.addressbook
 -rw-r--r--    1 bozo      4602 May 25 13:58 data1.addressbook.bak
 -rw-r--r--    1 bozo       877 Dec 17  2000 employment.addressbook
 
 
 bash$ ls -al	      
 total 14
 drwxrwxr-x    2 bozo  bozo      1024 Aug 29 20:54 ./
 drwx------   52 bozo  bozo      3072 Aug 29 20:51 ../
 -rw-r--r--    1 bozo  bozo      4034 Jul 18 22:04 data1.addressbook
 -rw-r--r--    1 bozo  bozo      4602 May 25 13:58 data1.addressbook.bak
 -rw-r--r--    1 bozo  bozo       877 Dec 17  2000 employment.addressbook
 -rw-rw-r--    1 bozo  bozo         0 Aug 29 20:54 .hidden-file
 	        

作为目录名时,单个点(.)表示当前目录,两个点(..)表示上一级目录(译者注:或称为父目录)。

 bash$ pwd
 /home/bozo/projects
 
 bash$ cd .
 bash$ pwd
 /home/bozo/projects
 
 bash$ cd ..
 bash$ pwd
 /home/bozo/
 	        

单点(.)文件名常常被当作文件移动命令的目的路径.

 bash$ cp /home/bozo/current_work/junk/* .
 	        

.

点(.)字符匹配.作为正则表达式的一部分,匹配字符时,单点(.)表示匹配任意一个字符。

"

部分引用[双引号]. "STRING"的引用会使STRING里的特殊字符能够被解释。请参考第五章.

'

完全引用[单引号]. 'STRING'能引用STRING里的所有字符(包括特殊字符也会被原样引用). 这是一个比使用双引号(“)更强的引用。 参考第5章.

,

逗号操作符[逗号]. 逗号操作符用于连接一连串的数学表达式。这一串的数学表达式每一个都被求值,但只有最后一个被返回。(译者注:换句话说,就是整个表达式的值取决于最后一个表达式的值。)
   1 let "t2 = ((a = 9, 15 / 3))"  # Set "a = 9" and "t2 = 15 / 3"

\

转义符[后斜杠].用于单个字符的引用机制。

\X "转义"字符为X.它有"引用"X的作用,也等同于直接在单引号里的'X'.\符也可以用于引用双引号(")和单引号('),这时双引号和单引号就表示普通的字符,而不是表示引用了。

参考第五章对转义字符的更深入的解释。

/

文件路径的分隔符[前斜杠]. 分隔一个文件路径的各个部分。(就像/home/bozo/projects/Makefile).

它也是算术操作符中的除法.

`

命令替换.`command` 结构使字符(`)[译者注:这个字符不是单引号,而是在标准美国键盘上的ESC键下面,在字符1左边,在TAB键上面的那个键,要特别留心]引住的命令(command)执行结果能赋值给一个变量。它也被称为后引号(backquotes)或是斜引号(backticks).

:

空命令[冒号]. 这个命令意思是空操作(即什么操作也不做). 它一般被认为是和shell的内建命令true是一样的。冒号":" 命令是Bash自身内建的, and its它的退出状态码是真(即0)。[译者注:shell中真用数字0表示].

   1 :
   2 echo $?   # 0

死循环可以这么写:

   1 while :
   2 do
   3    operation-1
   4    operation-2
   5    ...
   6    operation-n
   7 done
   8 
   9 # 等同于:
  10 #    while true
  11 #    do
  12 #      ...
  13 #    done

在if/then的测试结构中用作占位符:

   1 if condition
   2 then :   # 什么也不做的分支
   3 else
   4    take-some-action
   5 fi

在必须要有两元操作的地方作为一个分隔符, 参考例子 8-2默认参数.

   1 : ${username=`whoami`}
   2 # ${username=`whoami`}   如果没有开头的:,将会出错
   3 #                        除非"username"是一个外部命令或是内建命令...

here document中的一个命令作为一个分隔符. 参考例子 17-10.

参数替换中为字符串变量赋值 (就像例子 9-14).
   1 : ${HOSTNAME?} ${USER?} ${MAIL?}
   2 #  如果列出的一个或多个基本的环境变量没有设置,
   3 #+ 将打印出错信息。

变量扩展/子串代换.

重定向操作符(>)连用, 可以把一个文件的长度截短为零,文件的权限不变。如果文件不存在,则会创建一个新文件。
   1 : > data.xxx   # 文件"data.xxx"现在长度为0了	      
   2 
   3 # 作用相同于:cat /dev/null >data.xxx(译者注:echo >data.xxx也可以)
   4 # 但是,用NULL(:)操作符不会产生一个新的进程,因为NULL操作符是内建的。
请参考例子 12-14.

和添加重定向操作符(>>)连用(: >> target_file).如果目标文件存在则什么也没有发生,如果目标文件不存在,则创建它。

这只能应用在普通文件中,不能用在管道,符号链接和其他的特殊文件。

虽然这是不被推荐的,但是NULL操作符(:)也可以用于开始注释一行。使用#来注释一行将会使Bash不会检查这行后面的语法是否有错,因此#注释几乎可以出现任何的东西。但是,对于用NULL操作符(:)注释的行则不是这样。

下面一个会产生错误的注释。
   1 : This is a comment that generates an error, ( if [ $x -eq 3] ).

字符”:”也用于域分割符。比如说在/etc/passwd和环境变量$PATH里.
 bash$ echo $PATH
 /usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/games

!

取反一个测试结果或退出状态[感叹号]. 取反操作符(!)取反一个命令的退出状态(参考例子 6-2).它也取反一个测试操作。例如,它能改相等符( = )为不等符( != ).取反操作符(!)是一个Bash的关键字.

在不同的环境里,感叹号也可以出现在间接变量引用.

还有一种环境里,在命令行中,感叹号(!)调用属于历史命令机制的调用(详细请看附录 J).值得一提的是,在一个脚本里,命令历史机制是被禁止的。

*

通配符[星号].星号(*)字符在用于匹配文件名扩展的一个通配符.它自动匹配给定的目录下的每一个文件。

 bash$ echo *
 abs-book.sgml add-drive.sh agram.sh alias.sh
 	      

星号(*)也用于正则表达式中匹配任意数字字符。.

*

算术操作符. 在计算时,星号(*)表示乘法运算符。两个星号(**)表示求幂运算符

?

测试操作符.在一些表达式中,问号(?)表示一个条件测试.

双括号结构里,问号(?)表示C风格的三元操作符.请参考例子 9-30.

参数替换表达式里,问号(?)测试一个变量是否被设置了值.

?

通配符. 字符?被用于文件名扩展特性的文件名表达式的单字符匹配,同时也在扩展正则表达式中匹配任意一个字符.

$

变量替换 (引用一个变量的内容).
   1 var1=5
   2 var2=23skidoo
   3 
   4 echo $var1     # 5
   5 echo $var2     # 23skidoo

一个变量名前面加一个$字符前缀表示引用该变量的内容。

$

行的结尾. 正则表达式里,一个$字符表示匹配一行的结尾。

 
${}
$*, $@
$?

保存退出码值的变量. 变量$?保存了一个命令,一个函数,或一个脚本的退出状态码的值。

$$

进程ID变量. 变量$$保存了脚本运行时的进程ID值。

()

命令组.
   1 (a=hello; echo $a)

一组由圆括号括起来的命令是新开一个子shell来执行的.

因为是在子shell里执行,在圆括号里的变量不能被脚本的其他部分访问。因为父进程(即脚本进程)不能存取子进程(即子shell)创建的变量。(译者注:读者若对这部分内容感兴趣,可以参考stevens的<<Advance Unix Environment Programing>>一书中对进程的描述。).
   1 a=123
   2 ( a=321; )	      
   3 
   4 echo "a = $a"   # a = 123
   5 # 在圆括号里的变量"a"实际上是一个局部变量,作用局域只是在圆括号内用于数组始初化

数组初始化.
   1 Array=(element1 element2 element3)

{xxx,yyy,zzz,...}

扩展支持.
   1 cat {file1,file2,file3} > combined_file
   2 # 连接file1,file2,和file3的内容并写到文件combined_file里去.
   3 
   4 
   5 cp file22.{txt,backup}
   6 # 拷贝"file22.txt"内容为"file22.backup"

一个命令可以在文件名扩展中从逗号分隔的各模式来扩展参数列表。 [1] 文件名将会依照列表中逗号分隔开的模式匹配扩展。

在扩展中的所有模式都不能包含空白字符,除非空白字符是被转义或引用的。

echo {file1,file2}\ :{\ A," B",' C'}

file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C

{}

代码块[花括号]. 这个结构也是一组命令代码块,事实上,它是匿名的函数。然而与一个函数所不同的,在代码块里的变量仍然能被脚本后面的代码访问。

 bash$ { local a;
	      a=123; }
 bash: local: can only be used in a
function
 	      

   1 a=123
   2 { a=321; }
   3 echo "a = $a"   # a = 321   (结果是在代码块里的值)
   4 
   5 # 多谢, S.C.

由花括号括起的代码块可以引起输入输出的I/O重定向


例子 3-1. 代码块与I/O重定向

   1 #!/bin/bash
   2 # 从/etc/fstab文件里按一次一行地读.
   3 
   4 File=/etc/fstab
   5 
   6 {
   7 read line1
   8 read line2
   9 } < $File
  10 
  11 echo "First line in $File is:"
  12 echo "$line1"
  13 echo
  14 echo "Second line in $File is:"
  15 echo "$line2"
  16 
  17 exit 0
  18 
  19 # 现在,你如何解析每一行的分割符?
  20 # 提示: 使用awk.


例子 3-2. 把一个代码块的结果写进一个文件

   1 #!/bin/bash
   2 # rpm-check.sh
   3 
   4 # 查询一个rpm安装包的描述,软件清单,和是否它能够被安装.
   5 # 并把结果保存到一个文件中.
   6 # 
   7 # 这个脚本使用一个代码块来举例说明。
   8 
   9 SUCCESS=0
  10 E_NOARGS=65
  11 
  12 if [ -z "$1" ]
  13 then
  14   echo "Usage: `basename $0` rpm-file"
  15   exit $E_NOARGS
  16 fi  
  17 
  18 { 
  19   echo
  20   echo "Archive Description:"
  21   rpm -qpi $1       # 查询软件包的描述.
  22   echo
  23   echo "Archive Listing:"
  24   rpm -qpl $1       # 查询软件包中的软件清单.
  25   echo
  26   rpm -i --test $1  # 查询该软件包能否被安装.
  27   if [ "$?" -eq $SUCCESS ]
  28   then
  29     echo "$1 can be installed."
  30   else
  31     echo "$1 cannot be installed."
  32   fi  
  33   echo
  34 } > "$1.test"       # 把代码块的所有输出重定向到一个文件中。
  35 
  36 echo "Results of rpm test in file $1.test"
  37 
  38 # 参考rpm的man手册来理解上面所用的选项。
  39 
  40 exit 0

不像一个用圆括号括起来的命令组,一个用花括号括起的代码块不会以一个子shell运行。[2]

{} \;

路径名. 基本上用于find命令里.它不是shell内建的.

分号";"结束find命令中-exec选项的命令序列.它应该转义一下以免被shell误解释。

[ ]

测试.

测试[ ]中的表达式. 注意[是shell内建的测试的一部分(同义于测试),并非 是外部命令/usr/bin/test的链接.

[[ ]]

测试.

测试[[ ]]之中的表达式(shell的关键字).

参考[[ ... ]]结构的讨论.

[ ]

数组元素.

数组的上下文中,方括号表示数组的每个元素的数字编号.
   1 Array[1]=slot_1
   2 echo ${Array[1]}

[ ]

字符集的范围.

用于正则表达式的一部分,方括号描述一个匹配的字符集范围.

(( ))

整数扩展.

扩展并计算(( ))里的整数表达式[译者注:粗心的读者要注意了,是整数计算,可不能用来做浮点计算].

参考(( ... ))结构的讨论.

> &> >& >> <

scriptname >filename重定向scriptname的输出到文件filename中去. 如果文件filename存在则将会被覆盖.

command &>filename 会重定向命令command标准输出(stdout)和标准错误(stderr)到文件filename中.

command >&2 把命令command的标准输出(stdout)重定向到标准错误(stderr).

scriptname >>filename appends把脚本scriptname的输出追加到文件filename.如果filename不存在,则它会被创建.

(command)>

<(command)

在不同的上下文中, 字符 "<"">"会被当作字符比较操作符.

在另一种不同的上下文中, 字符"<"">"被当作整数比较操作符. 请参考例子 12-9.

<<

here document中使用的重定向.

<<<

here string中使用的重定向.

<, >

ASCII比较.
   1 veg1=carrots
   2 veg2=tomatoes
   3 
   4 if [[ "$veg1" < "$veg2" ]]
   5 then
   6   echo "Although $veg1 precede $veg2 in the dictionary,"
   7   echo "this implies nothing about my culinary preferences."
   8 else
   9   echo "What kind of dictionary are you using, anyhow?"
  10 fi

\<, \>

bash$ grep '\<the\>' textfile

|

管道. 把上一个命令的输出传给下一个命令,或是shell. 这是连接命令的一种方法.

   1 echo ls -l | sh
   2 #  把"echo ls -l"的输出传给shell,
   3 #+ 这等同与直接的"ls -l".
   4 
   5 
   6 cat *.lst | sort | uniq
   7 # 合并且排序所有的".lst"文件,然后删除多余的相同行.

一个命令或一组命令的输出可以由管道传给一个脚本.
   1 #!/bin/bash
   2 # uppercase.sh : 把输入字符改为大写.
   3 
   4 tr 'a-z' 'A-Z'
   5 #  字母的范围一定要引号引起来,
   6 #+ 这样才能保护文件名而不会被扩展成单个字母的文件名.
   7 
   8 exit 0
现在,让我们把ls -l的输出用管道与这个脚本连起来.
 bash$ ls -l | ./uppercase.sh
 -RW-RW-R--    1 BOZO  BOZO       109 APR  7 19:49 1.TXT
 -RW-RW-R--    1 BOZO  BOZO       109 APR 14 16:48 2.TXT
 -RW-R--R--    1 BOZO  BOZO       725 APR 20 20:56 DATA-FILE
 	      

管道里的每一个进程的标准输出都被当成下一个命令的标准输入. 如果不是这种情况,那么数据流会阻塞,并且管道不会引起预期的效果。
   1 cat file1 file2 | ls -l | sort
   2 # 来自"cat file1 file2"的输出会消失.

管道以子进程来运行, 因此不能引起变量的改变。
   1 variable="initial_value"
   2 echo "new_value" | read variable
   3 echo "variable = $variable"     # variable = initial_value

如果在管道中的一个命令失败了,会过早的终结整个管道的执行。这称为管道破坏(broken pipe),这时会发送一个叫SIGPIPE信号.

>|

强迫重定向 (即使noclobber选项设置). 这会强迫覆盖一个存在的文件.

||

或逻辑操作符. 在一个测试结构中,||操作符当测试条件的任何一个为真时返回0 (成功)的标志.

&

在后台运行作业. 一个后面跟一个&的命令会在后台运行.

 bash$ sleep 10 &
 [1] 850
 [1]+  Done                    sleep 10
 	      

在一个脚本里,在后台运行的命令或是偶数的循环可以在后台运行.


例子 3-3. 在后台运行一个循环

   1 #!/bin/bash
   2 # background-loop.sh
   3 
   4 for i in 1 2 3 4 5 6 7 8 9 10            # 第一个循环.
   5 do
   6   echo -n "$i "
   7 done & # 把这个循环放到后台去.
   8        # 它有时会后于第二个循环执行.
   9 
  10 echo   # 这个'echo'有时不会打印出来.
  11 
  12 for i in 11 12 13 14 15 16 17 18 19 20   # 第二个循环.
  13 do
  14   echo -n "$i "
  15 done  
  16 
  17 echo   # 这个'echo'有时不会打印出来.
  18 
  19 # ======================================================
  20 
  21 # 这个脚本的输出是:
  22 # 1 2 3 4 5 6 7 8 9 10 
  23 # 11 12 13 14 15 16 17 18 19 20 
  24 
  25 # 然而有时你也有可能得到如下的输出:
  26 # 11 12 13 14 15 16 17 18 19 20 
  27 # 1 2 3 4 5 6 7 8 9 10 bozo $
  28 # (第二个 'echo'没有执行. 为什么?)
  29 
  30 # 偶尔也会:
  31 # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
  32 # (第一个 'echo' 没有执行. 为什么?)
  33 
  34 # 非常罕有的情况可能是:
  35 # 11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20 
  36 # 前台的循环抢占了后台的循环输出.
  37 
  38 exit 0
  39 
  40 #  Nasimuddin Ansari 建议在第6行和第14行的:echo -n "$i" 加入sleep 1
  41 #+ 将会更有趣
  42 #

脚本中在一条在后台运行的命令可能会引起脚本悬挂,等待一个击键动作。幸运的是,有一个补救的办法.

&&

与逻辑操作符. 测试结构中,&&操作只在测试条件两者 都为真时会返回0(成功).

-

选项, 前缀. 用于一个命令或过滤器的选项标志.或用于一个操作符的前缀.

COMMAND -[选项1][选项2][...]

ls -al

sort -dfu $filename

set -- $variable

   1 if [ $file1 -ot $file2 ]
   2 then
   3   echo "File $file1 is older than $file2."
   4 fi
   5 
   6 if [ "$a" -eq "$b" ]
   7 then
   8   echo "$a is equal to $b."
   9 fi
  10 
  11 if [ "$c" -eq 24 -a "$d" -eq 47 ]
  12 then
  13   echo "$c equals 24 and $d equals 47."
  14 fi

-

用于stdin或stdout重定向的源或目的[dash].

   1 (cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
   2 # 把整个目录树从一个目录移到另外一个目录
   3 # [谦逊的 Alan Cox <a.cox@swansea.ac.uk>,作了一些修改]
   4 
   5 # 1) cd /source/directory    源目录,这是要移动的目录所在地.
   6 # 2) &&                     逻辑与: 如果'cd'命令操作成功,然后就执行下一条命令.
   7 # 3) tar cf - .              tar命令的'c'选项创建一个新的归档文件,
   8 #                            而'f'(file)选项,后跟一个'-'表示创建的目标文件是标准输出,
   9 #                            并且要操作的源目录是当前目录 ('.').
  10 # 4) |                       然后由管道输出...
  11 # 5) ( ... )                 一个子shell
  12 # 6) cd /dest/directory      将当前目录切换到目的目录.
  13 # 7) &&                     逻辑与,和上面的解释一样
  14 # 8) tar xpvf -              解开归档文件('x'),保持文件属主和文件的权限('p'),
  15 #                            并且把输出的详细信息打印到标准输出 ('v'),
  16 #                            从标准输入读('f'后跟'-').
  17 #
  18 #                            注意'x'是一个命令,而'p', 'v', 'f'是选项.
  19 # 哇!
  20 
  21 
  22 
  23 # 更优雅的,但作用一样的:
  24 #   cd source/directory
  25 #   tar cf - . | (cd ../dest/directory; tar xpvf -)
  26 #
  27 #     也可以这样:
  28 # cp -a /source/directory/* /dest/directory
  29 #     或:
  30 # cp -a /source/directory/* /source/directory/.[^.]* /dest/directory
  31 #     如果在/source/directory有隐藏文件.

   1 bunzip2 linux-2.6.13.tar.bz2 | tar xvf -
   2 # --  解压tar文件  --    | --然后把结果传给"tar"--
   3 # 如果"tar"没有打上处理"bunzip2"程序的补丁,
   4 # 就需要用管道连接两个不连续的步骤.
   5 # 这个练习的目的是解压内核源码包.

注意"-"环境不是一个Bash操作符提供的,而是被由一些写标准输出的UNIX软件包来验证的,比如tar, cat,等等.

 bash$ echo "whatever" | cat -
 whatever 

当希望提供一个文件名时, '-' 重定向输出到标准输出(有时像tar cf),或者从标准输入接受输入,就好像它们是一个文件一样 . 这是在管道中使用文件导向(file-oriented)软件包作为一个过滤器的方法.

 bash$ file
 Usage: file [-bciknvzL] [-f namefile] [-m magicfiles] file...
 	      
只在命令行单独给出命令file,会引起一个错误信息.

加一个"-"来看看结果,这会使shell等候用户的输入
 bash$ file -
 abc
 standard input:              ASCII text
 
 
 
 bash$ file -
 #!/bin/bash
 standard input:              Bourne-Again shell script text executable
 	      
现在命令从标准输入接受输入并分析它.

"-"能被用来把标准输出由管道输出到其他的命令.这样就允许使用在文件开头增加几行的技巧.

使用 diff 来比较一个文件和另一个文件的某一段:

grep Linux file1 | diff file2 -

最后Finally,再展示一个使用-tar命令的真实例子.


例子 3-4. 备份前24小时被修改的文件

   1 #!/bin/bash
   2 
   3 #  备份当前目录下所有前24小时被修改的文件为一个归档压缩包(归档并且压缩)
   4 #
   5 
   6 BACKUPFILE=backup-$(date +%m-%d-%Y)
   7 #                 在备份文件中嵌入日期.
   8 #                 多谢Joshua Tschida的这个主意.
   9 archive=${1:-$BACKUPFILE}
  10 #  如果没有在命令行上指定备份的归档文件名,
  11 #+ 会以"backup-MM-DD-YYYY.tar.gz."作为默认的文件名
  12 
  13 tar cvf - `find . -mtime -1 -type f -print` > $archive.tar
  14 gzip $archive.tar
  15 echo "Directory $PWD backed up in archive file \"$archive.tar.gz\"."
  16 
  17 
  18 #  Stephane Chazelas指出:如果有许多文件被找到
  19 #+ 或任何一个文件名中包含有空白字符
  20 #+ 上面的代码将会失败.
  21 
  22 # 他建议用下面的代码:
  23 # -------------------------------------------------------------------
  24 #   find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archive.tar"
  25 #      using the GNU version of "find".
  26 
  27 
  28 #   find . -mtime -1 -type f -exec tar rvf "$archive.tar" '{}' \;
  29 #         portable to other UNIX flavors, but much slower.
  30 # -------------------------------------------------------------------
  31 
  32 
  33 exit 0

以"-"字符开头为文件名的文件当加上"-"的定向操作符时可能会引起问题.脚本应该检查这种情况并且给这种文件增加合适的路径前缀,例如 ./-FILENAME, $PWD/-FILENAME, 或$PATHNAME/-FILENAME.

如果一个变量的值以-开头,同样也可能会产生问题.
   1 var="-n"
   2 echo $var		
   3 # 和"echo -n"一样,什么也不会输出.

-

先前的工作目录. 命令cd - 可以回到原来的工作目录.它使用了$OLDPWD 环境变量.

不要弄混了这儿使用的"-"和上面刚讨论的"-"重定向操作符.对于"-"字符的解释应依赖于它出现的环境.

-

负号或减号. 减号用于算术操作.

=

等号. 赋值操作符
   1 a=28
   2 echo $a   # 28

不同的上下文, "="是一个字符串比较操作符.

+

加. 算术操作符加法.

不同的上下文中, +是一个正则表达式操作符.

+

选项. 一个命令或是过滤器的选项标志.

一些命令和内建命令+来启用一些选项,用-来禁用它们.

%

取模. 取模 (一次除法的余数) 算术操作.

不同的上下文中,%是一个模式匹配 操作符.

~

主目录或称为家目录[波浪号]. 它与内部变量 $HOME 是一致的. ~bozo是bozo'的主目录,而ls ~bozo 会列出此目录的内容. ~/ 是当前用户的主目录,并且ls ~/ 会列出此目录的内容.
 bash$ echo ~bozo
 /home/bozo
 
 bash$ echo ~
 /home/bozo
 
 bash$ echo ~/
 /home/bozo/
 
 bash$ echo ~:
 /home/bozo:
 
 bash$ echo ~nonexistent-user
 ~nonexistent-user
 	      

~+

当前工作目录. 它与外部变量$PWD是一致的.

~-

先前的工作目录. 它与外部变量$OLDPWD是一致的.

=~

正则表达式匹配. 这个操作符由bash 版本3引入.

^

行首.在正则表达式中, 字符"^"表达匹配一个文本行的开头.

控制字符

更改终端行为或文本显示. 控制字符都是以CONTROL + key的组合键.

在脚本文件中控制字符是不起作用的.

  • Ctl-B

    退格 (非破坏性的).

  • Ctl-C

    中断. 终结一个前台作业.

  • Ctl-D

    从一个shell中退出 (类似于exit).

    "EOF" (文件结尾:end of file).它也用于表示标准输入(stdin)的结束.

    在控制台或xterm 窗口输入文本时, Ctl-D删除在光标下的字符.如果没有字符存在,Ctl-D 则会登录出该会话. 在一个xterm窗口中,则会产生关闭此窗口的效果。

  • Ctl-G

    "哔" (beep).在一些老式的打字机终端上,它会响一下铃.

  • Ctl-H

    "杀掉" (破坏性的退格). 删除光标前的一个字符===.

       1 #!/bin/bash
       2 # 在一个字符串里嵌入 Ctl-H.
       3 
       4 a="^H^H"                  # 两个 Ctl-H (退格).
       5 echo "abcdef"             # abcdef
       6 echo -n "abcdef$a "       # abcd f
       7 #以一个空格结尾  ^              ^ 退二格.
       8 echo -n "abcdef$a"        # abcdef
       9 #  现在没有尾部的空格            不退格了 (为什么?).
      10                           # 结果和预料的不一样.
      11 echo; echo

  • Ctl-I

    水平制表符.

  • Ctl-J

    新行(换一行并到行首).

  • Ctl-K

    垂直制表符.

    在控制台或xterm 窗口输入文本时, Ctl-K 会删除从光标所在处到行尾的所有字符。

  • Ctl-L

    清屏 (重绘屏幕,清除前面的打印信息).这与clear命令作用相同.

  • Ctl-M

    回车.

       1 #!/bin/bash
       2 # 多谢Lee Maschmeyer的例子.
       3 
       4 read -n 1 -s -p $'Control-M leaves cursor at beginning of this line. Press Enter. \x0d'
       5                                   # 是的, '0d'是Control-M的十六进制值.
       6 echo >&2   #  '-s'使所有被键入的字符都不回显,
       7            #+ 所以需要明确地键入新行.
       8 
       9 read -n 1 -s -p $'Control-J leaves cursor on next line. \x0a'
      10 echo >&2   #  Control-J 是换行.
      11 
      12 ###
      13 
      14 read -n 1 -s -p $'And Control-K\x0bgoes straight down.'
      15 echo >&2   #  Control-K 是垂直制表符.
      16 
      17 # 展示垂直制表符作用的更好的例子是:
      18 
      19 var=$'\x0aThis is the bottom line\x0bThis is the top line\x0a'
      20 echo "$var"
      21 #  这和上面的例子一样工作.但是:
      22 echo "$var" | col
      23 #  这使行的右端比左端更高.
      24 #  这也解释了为什么我们以一个换行符开始和结束 --
      25 #+ 是为了避免屏幕显示混乱.
      26 
      27 # 这是Lee Maschmeyer的解释:
      28 # --------------------------
      29 #  在第一个垂直制表符例子中 . . . 垂直制表符使还未打印回车就直接垂直打印下来。
      30 #
      31 #  这只在不能“倒后”的设备里才成立,比如在Linux控制台,
      32 #
      33 #  垂直制表符真正的意图是能垂直地往上移,而不是往下移.
      34 #  可以在打印机里用于打印上标.
      35 #  这个要点的作用被用于仿效垂直制表符正确的功能.
      36 
      37 exit 0

  • Ctl-Q

    解冻 (XON).

    它解冻终端的标准输入.

  • Ctl-S

    挂起输入 (XOFF).

    它冻结终端的标准输入. (用 Ctl-Q 可恢复输入.)

  • Ctl-U

    删除从光标到行首的一行输入.在某些设置里,Ctl-U 删除整行的输入,而不管光标的位置.

  • Ctl-V

    当输入一个文本, Ctl-V允许插入控制字符。例如,下面两个命令是相等的:
       1 echo -e '\x0a'
       2 echo <Ctl-V><Ctl-J>

    Ctl-V 主要用于文本编辑.

  • Ctl-W

    当在控制台或一个xterm窗口敲入文本时, Ctl-W 会删除从在光标处往后的第一个空白符之间的内容.在某些设置里, Ctl-W 删除光标往后到第一个非文字和数字之间的字符.

  • Ctl-Z

    暂停一个前台作业.

空白

用做函数的分隔符,分隔命令或变量. 空白是由空格(spaces),制表(tabs),空行(blank lines),或是由上述的组合造成的. [3] 在一些上下文中,比如说变量赋值, 空白是不被允许的,它会导致语法错误.

空行不会影响脚本的行为,因此使用它可以很好的划分独立的函数段以增加可读性。

特殊变量$IFS用来分隔一些输入命令的分隔符,默认是空白符。

为了在字符串或在变量中产生空白,应该使用引用.

Notes

[1]

shell处理花括号扩展. 这个命令处理扩展的结果.

[2]

例外: 作用管道一部分的花括号代码块可以子shell中运行.
   1 ls | { read firstline; read secondline; }
   2 #  错误.在花括号内的代码块在一个子shell里运行,
   3 #+ 以致"ls"的输出不能把值传到代码块里.
   4 echo "First line is $firstline; second line is $secondline"  # 这不会工作.
   5 
   6 # 多谢, S.C.

[3]

一个换行("新行")也是一个空白字符。这就解释了为什么一个只包含一个换行符的空白行也被认为是空白了。