第 3 章. 特殊字符

是什么使一个字符变得特殊?如果它具有超出其字面意义的含义,一个元意义,那么我们将其称为特殊字符。与命令和关键字一起,特殊字符是 Bash 脚本的构建块。

脚本中及其他地方发现的特殊字符

#

注释。# 开头的行(#! 除外)是注释,不会被执行。

# This line is a comment.

注释也可以出现在命令结尾之后。

echo "A comment will follow." # Comment here.
#                            ^ Note whitespace before #

注释也可以跟在行首的空白字符之后。

     # A tab precedes this comment.

注释甚至可以嵌入在管道中。

initial=( `cat "$startfile" | sed -e '/#/d' | tr -d '\n' |\
# Delete lines containing '#' comment character.
           sed -e 's/\./\. /g' -e 's/_/_ /g'` )
# Excerpted from life.sh script

Caution

命令不能跟在同一行的注释之后。没有终止注释的方法,以便在同一行开始“活动代码”。为下一个命令使用新行。

Note

当然,引用转义#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#*:}       # Parameter substitution, not a comment.
echo $(( 2#101011 ))  # Base conversion, not a comment.

# Thanks, S.C.
标准的引用和转义字符(" ' \)转义 #。

某些模式匹配操作也使用 #

;

命令分隔符 [分号]。允许将两个或多个命令放在同一行。

echo hello; echo there


if [ -x "$filename" ]; then    #  Note the space after the semicolon.
#+                   ^^
  echo "File $filename exists."; cp $filename $filename.bak
else   #                       ^^
  echo "File $filename not found."; touch $filename
fi; echo "File test complete."

请注意,";" 有时需要被转义

;;

case 选项中的终止符 [双分号]。

case "$variable" in
  abc)  echo "\$variable = abc" ;;
  xyz)  echo "\$variable = xyz" ;;
esac

;;&, ;&
.

“点” 命令 [句点]。等同于 source(参见 示例 15-22)。这是一个 bash builtin

.

“点”,作为文件名的一部分。当处理文件名时,前导点是“隐藏”文件的前缀,ls 通常不会显示的文件。

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/* .
	        
将所有 “垃圾” 文件复制到 $PWD

.

“点” 字符匹配。匹配字符时,作为正则表达式的一部分,“点” 匹配单个字符

"

部分引用 [双引号]。"STRING" 保留(免于解释)STRING 中大部分的特殊字符。参见第 5 章

'

完全引用 [单引号]。'STRING' 保留 STRING 中所有的特殊字符。这是一种比 "STRING" 更强的引用形式。参见第 5 章

,

逗号运算符逗号运算符 [1] 将一系列算术运算链接在一起。所有运算都会被求值,但只返回最后一个运算的结果。

let "t2 = ((a = 9, 15 / 3))"
# Set "a = 9" and "t2 = 15 / 3"

逗号运算符也可以连接字符串。

for file in /{,usr/}bin/*calc
#             ^    Find all executable files ending in "calc"
#+                 in /bin and /usr/bin directories.
do
        if [ -x "$file" ]
        then
          echo $file
        fi
done

# /bin/ipcalc
# /usr/bin/kcalc
# /usr/bin/oidcalc
# /usr/bin/oocalc


# Thank you, Rory Winston, for pointing this out.

,, ,

参数替换中的小写转换(在 Bash 的 版本 4 中添加)。

\

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

\X 转义字符 X。这具有 “引用” X 的效果,等同于 'X'\ 可用于引用 "',以便它们被字面地表达。

有关转义字符的深入解释,请参见第 5 章

/

文件名路径分隔符 [正斜杠]。分隔文件名的组成部分(如/home/bozo/projects/Makefile).

这也是除法算术运算符

`

命令替换`command` 结构使 command 的输出可用于变量赋值。这也称为 反引号或反撇号。

:

空命令 [冒号]。这是 shell 中 “NOP” 的等效项(no op,一个无操作的操作)。它可以被认为是 shell 内置命令 true 的同义词。":" 命令本身是一个 Bash builtin,其退出状态true (0)。

:
echo $?   # 0

无限循环

while :
do
   operation-1
   operation-2
   ...
   operation-n
done

# Same as:
#    while true
#    do
#      ...
#    done

if/then 测试中的占位符

if condition
then :   # Do nothing and branch ahead
else     # Or else ...
   take-some-action
fi

在需要二元运算的地方提供占位符,参见 示例 8-2默认参数

: ${username=`whoami`}
# ${username=`whoami`}   Gives an error without the leading :
#                        unless "username" is a command or builtin...

: ${1?"Usage: $0 ARGUMENT"}     # From "usage-message.sh example script.

here document 中需要命令的地方提供占位符。参见 示例 19-10

使用参数替换评估变量字符串(如 示例 10-7 中所示)。

: ${HOSTNAME?} ${USER?} ${MAIL?}
#  Prints error message
#+ if one or more of essential environmental variables not set.

变量扩展 / 子字符串替换.

> 重定向运算符 结合使用,将文件截断为零长度,而不更改其权限。如果该文件之前不存在,则创建它。

: > data.xxx   # File "data.xxx" now empty.	      

# Same effect as   cat /dev/null >data.xxx
# However, this does not fork a new process, since ":" is a builtin.
另请参见 示例 16-15

>> 重定向运算符结合使用,对预先存在的目标文件没有影响(: >> target_file)。如果该文件之前不存在,则创建它。

Note

这适用于常规文件,不适用于管道、符号链接和某些特殊文件。

可以用来开始注释行,尽管不推荐这样做。使用 # 作为注释会关闭该行剩余部分的错误检查,因此几乎任何内容都可以出现在注释中。但是,: 的情况并非如此。

: 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

冒号 可以接受作为函数名

:()
{
  echo "The name of this function is "$FUNCNAME" "
  # Why use a colon as a function name?
  # It's a way of obfuscating your code.
}

:

# The name of this function is :
这不是可移植的行为,因此不建议这样做。实际上,较新版本的 Bash 不允许这种用法。下划线 _ 可以工作。

冒号可以在本来为空的函数中充当占位符。

not_empty ()
{
  :
} # Contains a : (null command), and so is not empty.

!

反转(或否定)测试或退出状态的含义 [感叹号]。! 运算符反转它所应用的命令的退出状态(参见 示例 6-2)。它也反转测试运算符的含义。例如,这可以将 等于 ( = ) 的含义更改为 不等于 ( != )。! 运算符是 Bash 关键字

在不同的上下文中,! 也出现在 间接变量引用中。

在另一个上下文中,从命令行! 调用 Bash 历史机制(参见 附录 L)。请注意,在脚本中,历史机制被禁用。

*

通配符 [星号]。* 字符在 globbing 中充当文件名扩展的“通配符”。它本身匹配给定目录中的每个文件名。

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

* 也表示 正则表达式中的任意数量(或零个)字符

*

算术运算符在算术运算的上下文中,* 表示乘法。

** 双星号可以表示求幂运算符或扩展文件匹配globbing

?

测试运算符。在某些表达式中,? 表示对条件的测试。

双括号结构中,? 可以充当 C 风格三元运算符的元素。[2]

条件?真值结果:假值结果

(( var0 = var1<98?9:21 ))
#                ^ ^

# if [ "$var1" -lt 98 ]
# then
#   var0=9
# else
#   var0=21
# fi

参数替换表达式中,? 测试变量是否已设置

?

通配符。? 字符在 globbing 中充当单字符“通配符”,以及在 扩展正则表达式表示一个字符

$

变量替换(变量的内容)。

var1=5
var2=23skidoo

echo $var1     # 5
echo $var2     # 23skidoo

变量名称前的 $ 表示变量所持有的

$

行尾。正则表达式中,"$" 指向文本的行尾

${}
$' ... '

带引号的字符串扩展此结构将单个或多个转义的八进制或十六进制值扩展为 ASCII [3]Unicode 字符。

$*, $@
$?

退出状态变量。$? 变量保存命令、函数或脚本本身的退出状态

$$

进程 ID 变量。$$ 变量保存它出现的脚本的进程 ID [4]

()

命令组。

(a=hello; echo $a)

Important

括号内的命令列表圆括号启动一个子 shell

子 shell 内圆括号内的变量对于脚本的其余部分是不可见的。父进程,即脚本,无法读取在子进程(子 shell)中创建的变量

a=123
( a=321; )	      

echo "a = $a"   # a = 123
# "a" within parentheses acts like a local variable.

数组初始化。

Array=(element1 element2 element3)

{xxx,yyy,zzz,...}

大括号扩展。

echo \"{These,words,are,quoted}\"   # " prefix and suffix
# "These" "words" "are" "quoted"


cat {file1,file2,file3} > combined_file
# Concatenates the files file1, file2, and file3 into combined_file.

cp file22.{txt,backup}
# Copies "file22.txt" to "file22.backup"

命令可以对逗号分隔的文件规范列表进行操作,该列表位于大括号中。[5] 文件名扩展 (globbing) 应用于大括号之间的文件规范。

Caution

大括号内不允许有空格,除非空格被引用或转义。

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

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

{a..z}

扩展大括号扩展。

echo {a..z} # 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
# Echoes characters between a and z.

echo {0..3} # 0 1 2 3
# Echoes characters between 0 and 3.


base64_charset=( {A..Z} {a..z} {0..9} + / = )
# Initializing an array, using extended brace expansion.
# From vladz's "base64.sh" example script.

{a..z} 扩展大括号扩展结构是 Bash 版本 3 中引入的功能。

{}

代码块 [花括号]。也称为内联组,此结构实际上创建了一个匿名函数(没有名称的函数)。但是,与“标准” 函数不同,代码块内的变量对于脚本的其余部分仍然可见。

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

a=123
{ a=321; }
echo "a = $a"   # a = 321   (value inside code block)

# Thanks, S.C.

用花括号括起来的代码块可以进行 I/O 重定向

示例 3-1. 代码块和 I/O 重定向

#!/bin/bash
# Reading lines in /etc/fstab.

File=/etc/fstab

{
read line1
read line2
} < $File

echo "First line in $File is:"
echo "$line1"
echo
echo "Second line in $File is:"
echo "$line2"

exit 0

# Now, how do you parse the separate fields of each line?
# Hint: use awk, or . . .
# . . . Hans-Joerg Diers suggests using the "set" Bash builtin.

示例 3-2. 将代码块的输出保存到文件

#!/bin/bash
# rpm-check.sh

#  Queries an rpm file for description, listing,
#+ and whether it can be installed.
#  Saves output to a file.
# 
#  This script illustrates using a code block.

SUCCESS=0
E_NOARGS=65

if [ -z "$1" ]
then
  echo "Usage: `basename $0` rpm-file"
  exit $E_NOARGS
fi  

{ # Begin code block.
  echo
  echo "Archive Description:"
  rpm -qpi $1       # Query description.
  echo
  echo "Archive Listing:"
  rpm -qpl $1       # Query listing.
  echo
  rpm -i --test $1  # Query whether rpm file can be installed.
  if [ "$?" -eq $SUCCESS ]
  then
    echo "$1 can be installed."
  else
    echo "$1 cannot be installed."
  fi  
  echo              # End code block.
} > "$1.test"       # Redirects output of everything in block to file.

echo "Results of rpm test in file $1.test"

# See rpm man page for explanation of options.

exit 0

Note

与(圆括号)内的命令组不同,如上所述,由 {花括号} 括起来的代码块通常不会启动子 shell[6]

可以使用非标准 for 循环迭代代码块。

{}

文本占位符。xargs -i (替换字符串选项) 之后使用。{} 双花括号是输出文本的占位符。

ls . | xargs -i -t cp ./{} $1
#            ^^         ^^

# From "ex42.sh" (copydir.sh) example.

{} \;

路径名。主要在 find 结构中使用。这不是 shell builtin

Note

";" 结束-execfind 命令序列的选项。需要对其进行转义以防止 shell 解释。

[ ]

测试。

[ ] 之间的测试表达式。请注意,[ 是 shell builtin test 的一部分(也是它的同义词),不是指向外部命令的链接/usr/bin/test.

[[ ]]

test。

[[ ]] 之间的测试表达式。比单括号 [ ] 测试更灵活,这是一个 shell 关键字

请参阅关于 [[ ... ]] 结构的讨论。

[ ]

数组元素。

数组的上下文中,方括号分隔该数组的每个元素的编号。

Array[1]=slot_1
echo ${Array[1]}

[ ]

字符范围。

作为正则表达式的一部分,方括号划定要匹配的字符范围

$[ ... ]

整数扩展。

评估 $[ ] 之间的整数表达式。

a=3
b=7

echo $[$a+$b]   # 10
echo $[$a*$b]   # 21

请注意,此用法已弃用,并已被 (( ... )) 结构取代。

(( ))

整数扩展。

扩展和评估 (( )) 之间的整数表达式。

请参阅关于 (( ... )) 结构的讨论。

> &> >& >> < <>

scriptname >filenamescriptname的输出重定向到文件filename。覆盖filename如果文件已存在。

command &>filenamestdoutstderr都从command重定向到filename.

Note

这在测试条件时抑制输出非常有用。例如,让我们测试某个命令是否存在。

bash$ type bogus_command &>/dev/null



bash$ echo $?
1
                    

或在脚本中

command_test () { type "$1" &>/dev/null; }
#                                      ^

cmd=rmdir            # Legitimate command.
command_test $cmd; echo $?   # 0


cmd=bogus_command    # Illegitimate command
command_test $cmd; echo $?   # 1

command >&2重定向stdout都从command重定向到stderr.

scriptname >>filename追加scriptname的输出重定向到文件filename的输出。filename如果不存在,则创建它。

[i]<>filename打开文件filename进行读写,并将 文件描述符 i 分配给它。如果filename不存在,则创建它。

(command)>

<(command)

在不同的上下文中"<"">" 字符充当 字符串比较运算符

在另一个上下文中"<"">" 字符充当 整数比较运算符。另请参见 示例 16-9

<<

here document 中使用的重定向。

<<<

here string 中使用的重定向。

<, >

ASCII 比较.

veg1=carrots
veg2=tomatoes

if [[ "$veg1" < "$veg2" ]]
then
  echo "Although $veg1 precede $veg2 in the dictionary,"
  echo -n "this does not necessarily imply anything "
  echo "about my culinary preferences."
else
  echo "What kind of dictionary are you using, anyhow?"
fi

\<, \>

bash$grep '\<the\>' textfile

|

管道。将前一个命令的输出 (stdout) 传递到下一个命令或 shell 的输入 (stdin)。这是一种将命令链接在一起的方法。

echo ls -l | sh
#  Passes the output of "echo ls -l" to the shell,
#+ with the same result as a simple "ls -l".


cat *.lst | sort | uniq
# Merges and sorts all ".lst" files, then deletes duplicate lines.

命令或多个命令的输出可以通过管道传输到脚本。

#!/bin/bash
# uppercase.sh : Changes input to uppercase.

tr 'a-z' 'A-Z'
#  Letter ranges must be quoted
#+ to prevent filename generation from single-letter filenames.

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
	      

Note

管道中每个进程的stdout必须作为下一个进程的stdin读取。如果不是这种情况,则数据流将阻塞,并且管道将无法按预期运行。

cat file1 file2 | ls -l | sort
# The output from "cat file1 file2" disappears.

管道作为子进程运行,因此无法更改脚本变量。

variable="initial_value"
echo "new_value" | read variable
echo "variable = $variable"     # variable = initial_value

如果管道中的一个命令中止,这将过早终止管道的执行。这种情况称为管道破裂,它会发送一个SIGPIPE 信号.

>|

强制重定向(即使设置了 noclobber 选项)。这将强制覆盖现有文件。

||

OR 逻辑运算符测试结构中,如果链接的测试条件之一为真,则 || 运算符会导致返回 0(成功)。

&

在后台运行作业。后跟 & 的命令将在后台运行。

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

在脚本中,命令甚至 循环都可以在后台运行。

示例 3-3. 在后台运行循环

#!/bin/bash
# background-loop.sh

for i in 1 2 3 4 5 6 7 8 9 10            # First loop.
do
  echo -n "$i "
done & # Run this loop in background.
       # Will sometimes execute after second loop.

echo   # This 'echo' sometimes will not display.

for i in 11 12 13 14 15 16 17 18 19 20   # Second loop.
do
  echo -n "$i "
done  

echo   # This 'echo' sometimes will not display.

# ======================================================

# The expected output from the script:
# 1 2 3 4 5 6 7 8 9 10 
# 11 12 13 14 15 16 17 18 19 20 

# Sometimes, though, you get:
# 11 12 13 14 15 16 17 18 19 20 
# 1 2 3 4 5 6 7 8 9 10 bozo $
# (The second 'echo' doesn't execute. Why?)

# Occasionally also:
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# (The first 'echo' doesn't execute. Why?)

# Very rarely something like:
# 11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20 
# The foreground loop preempts the background one.

exit 0

#  Nasimuddin Ansari suggests adding    sleep 1
#+ after the   echo -n "$i"   in lines 6 and 14,
#+ for some real fun.

Caution

在脚本中后台运行的命令可能会导致脚本挂起,等待击键。幸运的是,有一个 补救措施

&&

AND 逻辑运算符测试结构中,只有当链接的测试条件为真时,&& 运算符才会导致返回 0(成功)。

-

选项,前缀。命令或过滤器的选项标志。运算符的前缀。参数替换默认参数的前缀。

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

ls -al

sort -dfu $filename

if [ $file1 -ot $file2 ]
then #      ^
  echo "File $file1 is older than $file2."
fi

if [ "$a" -eq "$b" ]
then #    ^
  echo "$a is equal to $b."
fi

if [ "$c" -eq 24 -a "$d" -eq 47 ]
then #    ^              ^
  echo "$c equals 24 and $d equals 47."
fi


param2=${param1:-$DEFAULTVAL}
#               ^

--

双破折号--为命令添加(逐字)选项的前缀。

sort --ignore-leading-blanks

Bash 内置命令一起使用时,它表示该特定命令的选项结束

Tip

这提供了一种方便的方法来删除名称以破折号开头的文件。

bash$ ls -l
-rw-r--r-- 1 bozo bozo 0 Nov 25 12:29 -badname


bash$ rm -- -badname

bash$ ls -l
total 0

双破折号也与 set 结合使用。

set -- $variable(如 示例 15-18 中所示)

-

从/到 stdinstdout 的重定向 [破折号]。

bash$ cat -
abc
abc

...

Ctl-D

正如预期的那样,cat -回显stdin,在这种情况下是键盘输入的用户输入,到stdout。但是,使用 - 的 I/O 重定向是否具有实际应用?

(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
# Move entire file tree from one directory to another
# [courtesy Alan Cox <a.cox@swansea.ac.uk>, with a minor change]

# 1) cd /source/directory
#    Source directory, where the files to be moved are.
# 2) &&
#   "And-list": if the 'cd' operation successful,
#    then execute the next command.
# 3) tar cf - .
#    The 'c' option 'tar' archiving command creates a new archive,
#    the 'f' (file) option, followed by '-' designates the target file
#    as stdout, and do it in current directory tree ('.').
# 4) |
#    Piped to ...
# 5) ( ... )
#    a subshell
# 6) cd /dest/directory
#    Change to the destination directory.
# 7) &&
#   "And-list", as above
# 8) tar xpvf -
#    Unarchive ('x'), preserve ownership and file permissions ('p'),
#    and send verbose messages to stdout ('v'),
#    reading data from stdin ('f' followed by '-').
#
#    Note that 'x' is a command, and 'p', 'v', 'f' are options.
#
# Whew!



# More elegant than, but equivalent to:
#   cd source/directory
#   tar cf - . | (cd ../dest/directory; tar xpvf -)
#
#     Also having same effect:
# cp -a /source/directory/* /dest/directory
#     Or:
# cp -a /source/directory/* /source/directory/.[^.]* /dest/directory
#     If there are hidden files in /source/directory.

bunzip2 -c linux-2.6.16.tar.bz2 | tar xvf -
#  --uncompress tar file--      | --then pass it to "tar"--
#  If "tar" has not been patched to handle "bunzip2",
#+ this needs to be done in two discrete steps, using a pipe.
#  The purpose of the exercise is to unarchive "bzipped" kernel source.

请注意,在这种上下文中,"-" 本身不是 Bash 运算符,而是某些 UNIX 实用程序识别的选项,这些实用程序写入stdout,例如 tarcat 等。

bash$ echo "whatever" | cat -
whatever 

在需要文件名的地方,-将输出重定向到stdout(有时在tar cf中看到),或从stdin而不是从文件中接受输入。 这是一种使用面向文件的实用程序作为管道中的过滤器的方法。

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
	      
现在,该命令接受来自stdin的输入并对其进行分析。

"-" 可用于将stdout通过管道传输到其他命令。这允许诸如将行预先添加到文件之类的技巧。

使用 diff 将文件与另一个文件的一部分进行比较

grep Linux file1 | diff file2 -

最后,一个使用-tar 的真实示例。

示例 3-4. 备份最近一天内更改的所有文件

#!/bin/bash

#  Backs up all files in current directory modified within last 24 hours
#+ in a "tarball" (tarred and gzipped file).

BACKUPFILE=backup-$(date +%m-%d-%Y)
#                 Embeds date in backup filename.
#                 Thanks, Joshua Tschida, for the idea.
archive=${1:-$BACKUPFILE}
#  If no backup-archive filename specified on command-line,
#+ it will default to "backup-MM-DD-YYYY.tar.gz."

tar cvf - `find . -mtime -1 -type f -print` > $archive.tar
gzip $archive.tar
echo "Directory $PWD backed up in archive file \"$archive.tar.gz\"."


#  Stephane Chazelas points out that the above code will fail
#+ if there are too many files found
#+ or if any filenames contain blank characters.

# He suggests the following alternatives:
# -------------------------------------------------------------------
#   find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archive.tar"
#      using the GNU version of "find".


#   find . -mtime -1 -type f -exec tar rvf "$archive.tar" '{}' \;
#         portable to other UNIX flavors, but much slower.
# -------------------------------------------------------------------


exit 0

Caution

"-" 开头的文件名在与 "-" 重定向运算符结合使用时可能会导致问题。脚本应检查此情况,并为此类文件名添加适当的前缀,例如./-FILENAME, $PWD/-FILENAME,或$PATHNAME/-FILENAME.

如果变量的值以-开头,则同样可能造成问题。

var="-n"
echo $var		
# Has the effect of "echo -n", and outputs nothing.

-

先前工作目录。cd - 命令更改为先前工作目录。这使用 $OLDPWD 环境变量

Caution

不要将在此意义上使用的 "-" 与刚刚讨论的 "-" 重定向运算符混淆。"-" 的解释取决于它出现的上下文。

-

减号。算术运算中的减号。

=

等于。赋值运算符

a=28
echo $a   # 28

不同的上下文中"="字符串比较运算符。

+

加号。加法 算术运算符

不同的上下文中+正则表达式运算符。

+

选项。命令或过滤器的选项标志。

某些命令和 builtins 使用+来启用某些选项,并使用-来禁用它们。在参数替换中,+为变量扩展到的备用值添加前缀。

%

模数模数(除法的余数)算术运算

let "z = 5 % 3"
echo $z  # 2

不同的上下文中%模式匹配运算符。

~

主目录 [波浪号]。这对应于 $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 中引入的。

^

行首。正则表达式中,"^" 指向文本的行首

^, ^^

参数替换中的大写转换(在 Bash 的 版本 4 中添加)。

控制字符

更改终端或文本显示的行为。控制字符是 CONTROL + 按键 组合(同时按下)。控制字符也可以用 八进制十六进制 表示法编写,后跟一个 转义符

控制字符在脚本中通常没有用。

  • Ctl-A

    将光标移动到文本行首(在命令行上)。

  • Ctl-B

    退格键(非破坏性)。

  • Ctl-C

    中断。终止前台作业。

  • Ctl-D

    从 shell 注销(类似于 exit)。

    EOF(文件结束符)。这也终止来自stdin.

    的输入。在控制台或 xterm 窗口中键入文本时,Ctl-D擦除光标下的字符。当没有字符时,Ctl-D注销会话,正如预期的那样。在 xterm 窗口中,这具有关闭窗口的效果。

  • Ctl-E

    将光标移动到文本行尾(在命令行上)。

  • Ctl-F

    将光标向前移动一个字符位置(在命令行上)。

  • Ctl-G

    BEL。在一些旧式电传打字终端上,这实际上会发出铃声。在 xterm 中,它可能会发出哔哔声。

  • Ctl-H

    删除(破坏性退格键)。擦除光标在退格时经过的字符。

    #!/bin/bash
    # Embedding Ctl-H in a string.
    
    a="^H^H"                  # Two Ctl-H's -- backspaces
                              # ctl-V ctl-H, using vi/vim
    echo "abcdef"             # abcdef
    echo
    echo -n "abcdef$a "       # abcd f
    #  Space at end  ^              ^  Backspaces twice.
    echo
    echo -n "abcdef$a"        # abcdef
    #  No space at end               ^ Doesn't backspace (why?).
                              # Results may not be quite as expected.
    echo; echo
    
    # Constantin Hagemeier suggests trying:
    # a=$'\010\010'
    # a=$'\b\b'
    # a=$'\x08\x08'
    # But, this does not change the results.
    
    ########################################
    
    # Now, try this.
    
    rubout="^H^H^H^H^H"       # 5 x Ctl-H.
    
    echo -n "12345678"
    sleep 2
    echo -n "$rubout"
    sleep 2

  • Ctl-I

    水平制表符.

  • Ctl-J

    换行符(换行)。在脚本中,也可以用八进制表示法('\012')或十六进制表示法('\x0a')表示。

  • Ctl-K

    垂直制表符.

    的输入。在控制台或 xterm 窗口中键入文本时,Ctl-K从光标下的字符擦除到行尾。在脚本中,Ctl-K可能会有不同的行为,如下面的 Lee Lee Maschmeyer 的示例所示。

  • Ctl-L

    换页(清除终端屏幕)。在终端中,这与 clear 命令具有相同的效果。当发送到打印机时,Ctl-L会导致前进到纸张末端。

  • Ctl-M

    回车符.

    #!/bin/bash
    # Thank you, Lee Maschmeyer, for this example.
    
    read -n 1 -s -p \
    $'Control-M leaves cursor at beginning of this line. Press Enter. \x0d'
               # Of course, '0d' is the hex equivalent of Control-M.
    echo >&2   #  The '-s' makes anything typed silent,
               #+ so it is necessary to go to new line explicitly.
    
    read -n 1 -s -p $'Control-J leaves cursor on next line. \x0a'
               #  '0a' is the hex equivalent of Control-J, linefeed.
    echo >&2
    
    ###
    
    read -n 1 -s -p $'And Control-K\x0bgoes straight down.'
    echo >&2   #  Control-K is vertical tab.
    
    # A better example of the effect of a vertical tab is:
    
    var=$'\x0aThis is the bottom line\x0bThis is the top line\x0a'
    echo "$var"
    #  This works the same way as the above example. However:
    echo "$var" | col
    #  This causes the right end of the line to be higher than the left end.
    #  It also explains why we started and ended with a line feed --
    #+ to avoid a garbled screen.
    
    # As Lee Maschmeyer explains:
    # --------------------------
    #  In the [first vertical tab example] . . . the vertical tab
    #+ makes the printing go straight down without a carriage return.
    #  This is true only on devices, such as the Linux console,
    #+ that can't go "backward."
    #  The real purpose of VT is to go straight UP, not down.
    #  It can be used to print superscripts on a printer.
    #  The col utility can be used to emulate the proper behavior of VT.
    
    exit 0

  • Ctl-N

    擦除从历史缓冲区 [8] 中调用的文本行(在命令行上)。

  • Ctl-O

    发出换行符(在命令行上)。

  • Ctl-P

    历史缓冲区中调用最后一个命令(在命令行上)。

  • Ctl-Q

    恢复 (XON).

    这恢复了stdin在终端中。

  • Ctl-R

    历史缓冲区中向后搜索文本(在命令行上)。

  • Ctl-S

    暂停 (XOFF).

    这冻结了stdin在终端中。(使用 Ctl-Q 恢复输入。)

  • Ctl-T

    将光标所在的字符与前一个字符的位置反转(在命令行上)。

  • Ctl-U

    擦除一行输入,从光标向后到行首。在某些设置中,Ctl-U擦除整行输入,无论光标位置如何

  • Ctl-V

    在输入文本时,Ctl-V允许插入控制字符。例如,以下两个是等效的

    echo -e '\x0a'
    echo <Ctl-V><Ctl-J>

    Ctl-V主要在文本编辑器中很有用。

  • Ctl-W

    在控制台或 xterm 窗口中键入文本时,Ctl-W从光标下的字符向后擦除到 空白字符 的第一个实例。在某些设置中,Ctl-W向后擦除到第一个非字母数字字符。

  • Ctl-X

    在某些文字处理程序中,剪切突出显示的文本并复制到剪贴板

  • Ctl-Y

    粘贴先前擦除的文本(使用Ctl-UCtl-W).

  • Ctl-Z

    暂停前台作业。

    替换某些文字处理应用程序中的操作。

    EOFMSDOS 文件系统中的(文件结束)字符。

空白字符

充当命令和/或变量之间的分隔符。空白字符由空格制表符空行或它们的任意组合组成。[9] 在某些上下文中,例如变量赋值,不允许使用空白字符,并会导致语法错误。

空行对脚本的操作没有影响,因此对于在视觉上分隔功能部分很有用。

$IFS,特殊变量,用于分隔某些命令的输入字段。它默认为空白字符。

要在字符串或变量中保留空白字符,请使用引用

UNIX 过滤器可以使用 POSIX 字符类 [:space:] 来定位和操作 空白字符

注释

[1]

运算符是执行操作的代理。一些示例是常见的 算术运算符+ - * /。在 Bash 中,运算符关键字 的概念之间存在一些重叠。

[2]

这更常被称为三元运算符。不幸的是,ternary 是一个难看的词。它发音不流畅,也没有阐明。它使人困惑。Trinary 是迄今为止更优雅的用法。

[3]

American Standard Code for Information Interchange(美国信息交换标准代码)。 这是一个用于编码文本字符(字母、数字和有限的符号集)的系统,这些字符被编码为 7 位数字,可以被计算机存储和处理。 许多 ASCII 字符在标准键盘上都有表示。

[4]

一个 PID,或进程 ID,是分配给正在运行的进程的数字。 可以使用 ps 命令查看正在运行的进程的 PID

定义一个 进程 是当前正在执行的命令(或程序),有时也称为 作业

[5]

shell 执行 花括号展开。 命令本身作用于展开的 结果

[6]

例外:作为管道一部分的花括号中的代码块 可能 作为 子shell 运行。

ls | { read firstline; read secondline; }
#  Error. The code block in braces runs as a subshell,
#+ so the output of "ls" cannot be passed to variables within the block.
echo "First line is $firstline; second line is $secondline"  # Won't work.

# Thanks, S.C.

[7]

正如古代 philtre 表示一种据称具有神奇转化能力的药剂一样,UNIX filter 也以(大致)类似的方式转换其目标。(如果程序员开发出在 Linux 机器上运行的 “love philtre”,则可能会赢得赞誉和荣誉。)

[8]

Bash 将先前从命令行发出的命令列表存储在 缓冲区 或内存空间中,以便使用 builtin history 命令进行调用。

[9]

换行符(newline)也是一个空白字符。 这解释了为什么一个 空行(仅由换行符组成)被视为空白。