Shell基础脚本-特殊字符

是什么让一个字符变得特殊呢?如果一个字符不仅具有字面意义,而且具有元意(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 ))  # 进制转换而非注释

因为引用符和转义符(" ' \)转义了#
一些模式匹配操作同样使用了#

( ; )

命令分隔符[分号],允许在同一行内放置两条或更多的命令

echo hello; echo there

if [ -x "$filename" ]; then    #  注意在分号以后有一个空格
#+                   ^^
  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

( ;;&, ;& )

case条件语句终止符(Bash4+ 版本)

( . )

句点可以作为文件名的一部分。如果它在文件名开头,那说明此文件是隐藏文件。使用不带参数的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/* .

复制所有的“垃圾文件”到当前目录

( . )

句点匹配符。在正则表达式中,点号意味着匹配任意单个字符

( '' )

部分引用[双引号]。在字符串中保留大部分特殊字符

( ' )

全引用[单引号]。在字符串中保留所有的特殊字符

( , )

逗号运算符。逗号运算符将一系列的算术表达式串联在一起。算术表达式依次被执行,但只返回最后一个表达式的值

let "t2 = ((a = 9, 15 / 3))"
# a被赋值为9,t2被赋值为15 / 3

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

for file in /{,usr/}bin/*calc
#             ^    在 /bin 与 /usr/bin 目录中
#+                 找到所有的以"calc"结尾的可执行文件
do
        if [ -x "$file" ]
        then
          echo $file
        fi
done

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

( ,, , )

在参数替换中进行小写字母转换(Bash4 新增)

( \ )

转义符[反斜杠]。转义某字符的标志。
( \x ) 转义了字符x。双引号""内的X与单引号内的X具有同样的效果。 转义符也可以用来转义"与',使它们表达其字面含义。

Shell脚本-引用,将更加深入的解释转义字符
地址:

( / )

文件路径分隔符[正斜杠]。起分割路径的作用(比如 /home/bozo/projects/Makefile)
它也在算术运算中充当除法运算符

( ` )

命令替换符。command 结构可以使得命令的输出结果赋值给一个变量。通常也被称作后引号(backquotes)或反引号(backticks)

( : )

空命令[冒号]。它在shell中等价于"NOP"(即no op,空操作)与shell内建命令true有同样的效果。它本身也是Bash的内建命令之一,返回值是true(0)

:
echo $?   # 返回0

在无限循环中的应用:

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

# 等价于
#    while true
#    do
#      ...
#    done

可在 if/then 中充当占位符:

if condition
then :   # 什么都不做,跳出判断执行下一条语句
else
   take-some-action
fi

在二元操作中作占位符: 查看样例 运算符相关话题-数字常量 或 默认参数部分

: ${username=`whoami`}
# ${username=`whoami`}   如果没有:就会报错
#                        除非 "username" 是系统命令或内建命令

: ${1?"Usage: $0 ARGUMENT"}     # 摘自样例脚本 "usage-message.sh"

查看样例:( Shell脚本-高级话题 嵌入文档-"匿名" 嵌入文档)了解空命令在here document中作为占位符的情况。
使用参数替换为字符串变量赋值 ( Shell脚本进阶-参数替换)

: ${HOSTNAME?} ${USER?} ${MAIL?}
#  如果其中一个或多个必要的环境变量没有被设置
#  将会打印错误

查看变量扩展或字符串替换章节了解空命令在其中的作用

与>重定向操作符结合,可以在不改变文件权限的情况下清空文件。如果文件不存在,那么将创建这个文件

: > data.xxx   # 文件 "data.xxx" 已被清空

# 与 cat /dev/null >data.xxx 作用相同
# 但是此操作不会产生一个新进程,因为 ":" 是shell内建命令。

与>>重定向操作符结合,将不会清空任何已存在的文件(: >> target_file)。如果文件不存在,将创建这个文件

提示:以上操作仅适用于普通文件,不适用于管道、符号链接和特殊文件

空命令可以用来作为一行注释的开头,尽管我们并不推荐这么做。使用 # 可以使解释器关闭该行的错误检测,所以几乎所有的内容都可以出现在注释#中。使用空命令却不是这样的:

: 这一行注释将会产生一个错误,( 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" "
  # 为什么要使用冒号作函数名?
  # 这是一种混淆代码的方法......
}

:
# 函数名是 :

这种写法并不具有可移植性,也不推荐使用。事实上,在Bash的最近的版本更新中已经禁用了这种用法。但我们还可以使用下划线 _来替代

冒号也可以作为非空函数的占位符

not_empty ()
{
  :
} # 含有空指令,这并不是一个空函数。

( ! )

取反(或否定)操作符[感叹号]。! 操作符反转已执行的命令的返回状态,它同时可以反转测试操作符的意义,例如可以将相等(=)反转成不等(!=)。它是一个Bash关键词。
在一些特殊场景下,它也会出现在间接变量引用中。
在另外一些特殊场景下,即在命令行下可以使用 ! 调用Bash的历史记录(附录 L)。需要注意的是,在脚本中,这个机制是被禁用的。

( * )

通配符[星号]。在文件匹配(globbing)操作时扩展文件名。如果它独立出现,则匹配该目录下的所有文件

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

在正则表达式中表示匹配任意多个(包括0)前个字符

( * )

算术运算符。在进行算术运算时,表示乘法运算
** 双星号可以表示乘方运算或扩展文件匹配

( ? )

测试操作符[问号]。在一些特定的语句中,? 表示一个条件测试。
在一个双圆括号结构中,? 可以表示一个类似C语言风格的三元(trinary)运算符的一个组成部分。
condition?result-if-true:result-if-false

(( 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

如果在变量名前有 ,则表示此变量的值 ( )
行结束符[EOF]。 在正则表达式中,$ 匹配行尾字符串

( ${} )

参数替换

( $'...' )

引用字符串扩展。这个结构将转义八进制或十六进制的值转换成ASCII或Unicode字符

位置参数

$*, $@  # 位置参数

( $? )

返回状态变量。此变量保存一个命令、一个函数或该脚本自身的返回状态

( $$ )

进程ID变量。此变量保存该运行脚本的进程ID

()

命令组 (a=hello; echo $a)
通过括号执行一系列命令会产生一个子shell(subshell)。 括号中的变量,即在子shell中的变量,在脚本的其他部分是不可见的。父进程脚本不能访问子进程(子shell)所创建的变量

a=123
( a=321; )

echo "a = $a"   # a = 123
# 在括号中的 "a" 就像个局部变量。

数组初始化

Array=(element1 element2 element3)

( {xxx,yyy,zzz,...} )

花括号扩展结构

echo \"{These,words,are,quoted}\"   # " 将作为单词的前缀和后缀
# "These" "words" "are" "quoted"
cat {file1,file2,file3} > combined_file
# 将 file1, file2 与 file3 拼接在一起后写入 combined_file 中。
cp file22.{txt,backup}
# 将 "file22.txt" 拷贝为 "file22.backup"

这个命令可以作用于花括号内由逗号分隔的文件描述列表。 文件名扩展(匹配)作用于大括号间的各个文件

除非被引用或被转义,否则空白符不应在花括号中出现

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
# 输出 a 到 z 之间所有的字母。

echo {0..3} # 0 1 2 3
# 输出 0 到 3 之间所有的数字。


base64_charset=( {A..Z} {a..z} {0..9} + / = )
# 使用扩展花括号初始化一个数组。

Bash第三版中引入了 {a..z} 扩展的花括号扩展结构

( {} )

代码块[花括号],又被称作内联组(inline group)。它实际上创建了一个匿名函数(anonymous function),即没有名字的函数。但是,不同于那些“标准”函数,代码块内的变量在脚本的其他部分仍旧是可见的

bash$ { local a;
              a=123; }
bash: local: can only be used in a
function
a=123
{ a=321; }
echo "a = $a"   # a = 321   (代码块内赋值)

代码块可以经由I/O重定向进行输入或输出

样例-1: 代码块及I/O重定向

#!/bin/bash
# 读取文件 /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

# 建议:使用Bash的内建命令 set。

样例-2:将代码块的输出保存至文件中

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

# 查询一个rpm文件的文件描述、包含文件列表,以及是否可以被安装。
# 将输出保存至文件。
#
# 这个脚本使用代码块来描述。

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 "Archive Listing:"
  rpm -qpl $1       # 查询文件列表。
  echo
  rpm -i --test $1  # 查询是否可以被安装。
  if [ "$?" -eq $SUCCESS ]
  then
    echo "$1 can be installed."
  else
    echo "$1 cannot be installed."
  fi  
  echo              # 代码块结束。
} > "$1.test"       # 输出重定向至文件。

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

# rpm各项参数的具体含义可查看man文档
exit 0

提示:
1)与由圆括号包裹起来的命令组不同,由花括号包裹起来的代码块不产生子进程。
2)也可以使用非标准的 for 循环语句来遍历代码块

{ } 文本占位符,在 xargs -i 后作为输出的占位符使用

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

( { } ; )

路径名。通常在 find 命令中使用,但这不是shell的内建命令
定义:路径名是包含完整路径的文件名,例如/home/bozo/Notes/Thursday/schedule.txt。我们通常又称之为绝对路径

提示:在执行find -exec时最后需要加上;,但是分号需要被转义以保证其不会被shell解释

( [ ] )

测试。在 [ ] 之间填写测试表达式。值得注意的是,[ 是shell内建命令 test 的一个组成部分,而不是外部命令 /usr/bin/test 的链接

( [[ ]] )

测试。在 [[ ]] 之间填写测试表达式。相比起单括号测试 ([ ]),它更加的灵活。它是一个shell的关键字。

( [ ] )

数组元素。在数组中,可以使用中括号的偏移量来用来访问数组中的每一个元素

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

( [ ] )

字符集、字符范围。 在正则表达式中,中括号用来匹配指定字符集或字符范围内的任意字符

( $[ ... ] )

整数扩展符。在 $[ ] 中可以计算整数的算术表达式

a=3
b=7

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

(( )) 整数扩展符。在 (( )) 中可以计算整数的算术表达式

( > &> >& >> < <> )

重定向
scriptname >filename 将脚本 scriptname 的输出重定向到 filename 中。如果文件存在,那么覆盖掉文件内容。
command &>filename 将命令 command 的标准输出(stdout) 和标准错误输出(stderr) 重定向到 filename

提示:重定向在用于清除测试条件的输出时特别有效。例如测试一个特定的命令是否存在

bash$ type bogus_command &>/dev/null

bash$ echo $?
1

或写在脚本中:

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

cmd=rmdir            # 存在的命令。
command_test $cmd; echo $?   # 返回0

cmd=bogus_command    # 不存在的命令。
command_test $cmd; echo $?   # 返回1

1)command >&2 将命令的标准输出重定向至标准错误输出
2)scriptname >>filename 将脚本 scriptname 的输出追加到 filename 文件末尾。如果文件不存在,那么将创建这个文件
3)[i]<>filename 打开文件 filename 用来读写,并且分配一个文件描述符i指向它。如果文件不存在,那么将创建这个文件
4)进程替换: (command)> <(command)
5)在某些情况下, "<" 与 ">" 将用作字符串比较
6)在另外一些情况下, "<" 与 ">" 将用作数字比较

( << )

在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

( <, > )

正则表达式中的单词边界(word boundary)

bash$ grep '\<the\>' textfile

( | )

管道(pipe)。管道可以将上一个命令的输出作为下一个命令的输入,或者直接输出到shell中。管道是一种可以将一系列命令连接在一起的绝妙方式

echo ls -l | sh
#  将 "echo ls -l" 的结果输出到shell中,
#  与直接输入 "ls -l" 的结果相同。

cat *.lst | sort | uniq
# 将所有后缀名为 lst 的文件合并后排序,接着删掉所有重复行。

管道是一种在进程间通信的典型方法。它将一个进程的输出作为另一个进程的输入。举一个经典的例子,像 cat 或者 echo 这样的命令,可以通过管道将它们产生的数据流导入到过滤器(filter)中。过滤器是可以用来处理输入流的命令

cat filename1filename2 | grep $search_word

命令的输出同样可以通过管道输入到脚本中

#!/bin/bash
# uppercase.sh : 将所有输入变成大写

tr 'a-z' 'A-Z'
#  为了防止产生单字符文件名,
#  必须使用单引号引用字符范围。

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

提示:在管道中,每一个进程的输出必须作为下个进程的输入被正确读入,如果不这样,数据流会被阻塞(block),管道就无法按照预期正常工作

cat file1 file2 | ls -l | sort
# "cat file1 file2" 的输出会消失。

管道是在一个子进程中运行的,因此它并不能修改父进程脚本中的变量

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

如果管道中的任意一个命令意外中止了,管道将会提前中断,我们称其为管道破裂(Broken Pipe)。出现这种情况,系统将发送一个 SIGPIPE 信号

( >| )

强制重定向。即使在 noclobber 选项被设置的情况下,重定向也会覆盖已存在的文件

( || )

或(OR)逻辑运算符。在测试结构中,任意一个测试条件为真,整个表达式为真。返回 0(成功标志位)

( & )

后台运行操作符。如果命令后带&,那么此命令将转至后台运行

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

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

样例-3. 在后台运行的循环

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

for i in 1 2 3 4 5 6 7 8 9 10            # 第一个循环
do
  echo -n "$i "
done & # 这个循环在后台运行。
       # 有时会在第二个循环结之后才执行此后台循环。

echo   # 此'echo' 有时不显示

for i in 11 12 13 14 15 16 17 18 19 20   # 第二个循环
do
  echo -n "$i "
done

echo   # 此'echo' 有时不显示

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

# 脚本期望输出结果:
# 1 2 3 4 5 6 7 8 9 10
# 11 12 13 14 15 16 17 18 19 20

# 一些情况下可能会输出:
# 11 12 13 14 15 16 17 18 19 20
# 1 2 3 4 5 6 7 8 9 10 bozo $
# 第二个 'echo' 没有被执行,为什么?

# 另外一些情况下可能会输出:
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# 第一个 'echo' 没有被执行,为什么?

# 非常罕见的情况下,可能会输出:
# 11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20
# 前台循环抢占(preempt)了后台循环。

exit 0

#  Nasimuddin Ansari 建议:在第6行和第14行的
#  echo -n "$i " 后增加 sleep 1,
#  会得到许多有趣的结果。

脚本在后台执行命令时可能因为等待键盘事件被挂起。幸运的是,有一套方案可以解决这个问题

( && )

与(AND)逻辑操作符。在测试结构中,所有测试条件都为真,表达式才为真,返回 0(成功标志位)

( - )

选项与前缀。它可以作为命令的选项标志,也可以作为一个操作符的前缀,也可以作为在参数代换中作为默认参数的前缀。
COMMAND -[Option1][Option2][..]
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内建命令一起使用时,意味着该命令选项的结束。
下面提供了一种删除文件名以横线开头文件的简单方法

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

( - )

重定向输入输出[短横线]

bash$ cat -
abc
abc

...

Ctrl-D

在这个例子中,cat - 输出由键盘读入的标准输入(stdin) 到 标准输出(stdout)。但是在真实应用的 I/O 重定向中是否有使用 '-'?

(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)

# 将整个文件树从一个目录移动到另一个目录。
# 感谢 Alan Cox <a.cox@swansea.ac.uk> 所作出的部分改动

# 1) cd /source/directory
#    工作目录定位到文件所属的源目录
# 2) &&
#    "与链":如果 'cd' 命令操作成功,那么执行下一条命令
# 3) tar cf - .
#    'tar c' (create 创建) 创建一份新的档案
#    'tar f -' (file 指定文件) 在 '-' 后指定一个目标文件作为输出
#    '.' 代表当前目录
# 4) |
#    通过管道进行重定向
# 5) ( ... )
#    在建立的子进程中执行命令
# 6) cd /dest/directory
#    工作目录定位到目标目录
# 7) &&
#    与 2) 相同
# 8) tar xpvf -
#    'tar x' 解压档案
#    'tar p' (preserve 保留) 保留档案内文件的所有权及文件权限
#    'tar v' (verbose 冗余) 发送全部信息到标准输出
#    'tar f -' (file 指定文件) 在 '-' 后指定一个目标文件作为输入
#
#    注意 'x' 是一个命令,而 'p', 'v', 'f' 是选项。

# 更加优雅的写法是:
#   cd source/directory
#   tar cf - . | (cd ../dest/directory; tar xpvf -)
#
# 同样可以写成:
#   cp -a /source/directory/* /dest/directory
# 或:
#   cp -a /source/directory/* /source/directory/.[^.]* /dest/directory
# 可以在源目录中有隐藏文件时使用
bunzip2 -c linux-2.6.16.tar.bz2 | tar xvf -
#  --未解压的 tar 文件--        | --将解压出的 tar 传递给 "tar"--
#  如果不使用管道让 "tar" 处理 "bunzip2" 得到的文件,
#  那么就需要使用单独的两步来完成。
#  目的是为了解压 "bzipped" 压缩的内核源代码。

下面的例子中,"-" 并不是一个Bash的操作符,它仅仅是 tar, cat 等一些特定UNIX命令中将结果输出到标准输出的选项

bash$ echo "whatever" | cat -
whatever

当需要文件名的时候,- 可以用来代替某个文件而重定向到标准输出(通常出现在 tar cf 中)或从 stdin 中接受数据。这是一种在管道中使用面向文件(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 -

最后介绍一个使用 - 的 tar 命令的实际案例
样例-4. 备份最近一天修改过的所有文件

#!/bin/bash

#  将当前目录下24小时之内修改过的所有文件备份成一个
#  "tarball" (经 tar 打包`与 gzip 压缩) 文件

BACKUPFILE=backup-$(date +%m-%d-%Y)
#                 在备份文件中嵌入时间

archive=${1:-$BACKUPFILE}
#  如果没有在命令行中特别制定备份格式,
#  那么将会默认设置为 "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 Chazeles 指出如果目录中有非常多的文件,
#  或文件名中包含空白符时,上面的代码会运行失败。

# 他建议使用以下的任意一种方法:
# -------------------------------------------------------------------
#   find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archive.tar"
#   使用了 GNU 版本的 "find" 命令。


#   find . -mtime -1 -type f -exec tar rvf "$archive.tar" '{}' \;
#   兼容其他的 UNIX 发行版,但是速度会比较慢
# -------------------------------------------------------------------


exit 0

以 "-" 开头的文件在和"-" 重定向操作符一起使用时可能会导致一些问题。因此合格的脚本必须首先检查这种情况。如果遇到,就需要给文件名加一个合适的前缀,比如 ./-FILENAME, PWD/-FILENAME 或者PATHNAME/-FILENAME

如果变量的值以 '-' 开头,也可能会造成类似问题

var='-n'
echo $var
# 等同于 "echo -n",不会输出任何东西。

( - )

先前的工作目录。使用 cd - 命令可以返回先前的工作目录。它实际上是使用了 $OLDPWD 环境变量。

不要将这里的 "-" 与先前的 "-" 重定位操作符混淆。"-" 的具体含义需要根据上下文来解释

( - )

减号。算术运算符中的减法标志

( = )

等号。赋值操作符

a=28
echo $a   # 28

在一些情况下,"=" 可以作为字符串比较操作符

( + )

加号。加法算术运算。
在一些情况下,+ 是作为正则表达式中的一个操作符

( + )

选项操作符。作为一个命令或过滤器的选项标记。
特定的一些指令和内建命令使用 + 启用特定的选项,使用 - 禁用特定的选项。在参数代换中,+ 是作为变量扩展的备用值(alternate value)的前缀

( % )

取模。取模操作运算符

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

( =~ )

正则表达式匹配

( ^ )

行起始符。在正则表达式中,"^" 代表一行文本的开始

( ^, ^^ )

参数替换中的大写转换符

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容