第五章:引用
引用意味著保護在引號中的字串. 引用在保護被引起字串中的特殊字元被shell或shell指令碼解釋或擴充套件. (如果一個字元能被特殊解釋為不同於它字面上表示的意思,那麼這個字元是“特殊”的,比如說萬用字元 -- *.)
bash$ ls -l [Vv]* -rw-rw-r-- 1 bozo bozo 324 Apr 2 15:05 VIEWDATA.BAT -rw-rw-r-- 1 bozo bozo 507 May 4 14:25 vartrace.sh -rw-rw-r-- 1 bozo bozo 539 Apr 14 17:11 viewdata.sh bash$ ls -l '[Vv]*' ls: [Vv]*: No such file or directory
在日常的演講和寫作中,我們使用雙引號("")引用一個短語,使短語被分開以示特別。在Bash指令碼中,當我們引用一個字串,也會把它分隔開以表示它原來字面上的意思。
某些程式和軟體包可以重新解釋或擴充套件引號裡的特殊字元。引號一個很重要的作用是保護命令列上的一個引數不被shell解釋,而把此引數傳遞給要執行的程式來處理它。
bash$ grep '[Ff]irst' *.txt
file1.txt:This is the first line of file1.txt.
file2.txt:This is the First line of file2.txt.
注意在Bash shell下如果沒有用引號的命令grep [Ff]irst *.txt
引號也能改掉echo's不換行的“習慣”。
bash$ echo $(ls -l)
total 8 -rw-rw-r-- 1 bozo bozo 130 Aug 21 12:57 t222.sh -rw-rw-r-- 1 bozo bozo 78 Aug 21 12:57 t71.sh
bash$ echo "$(ls -l)"
total 8
-rw-rw-r-- 1 bozo bozo 130 Aug 21 12:57 t222.sh
-rw-rw-r-- 1 bozo bozo 78 Aug 21 12:57 t71.sh
5.1. 引用變數
當要引用一個變數的值時,一般推薦使用雙引號。使用雙引號除了變數名
用雙引號還能使句子不被分割開. [4] 一個引數用雙引號引起來能使它被看做一個單元,這樣即使引數裡面包含有空白字元也不會被shell分割開了。
1 variable1="a variable containing five words"
2 COMMAND This is $variable1 # 用下面7個引數執行COMMAND命令:
3 # "This" "is" "a" "variable" "containing" "five" "words"
4
5 COMMAND "This is $variable1" # 用下面1個引數執行COMMAND命令:
6 # "This is a variable containing five words"
7
8
9 variable2="" # 空字串。
10
11 COMMAND $variable2 $variable2 $variable2 # 沒有帶引數執行COMMAND 命令
12 COMMAND "$variable2" "$variable2" "$variable2" # 用三個含空字串的引數執行COMMAND命令
13 COMMAND "$variable2 $variable2 $variable2" # 用一個包含兩個空白符的引數執行COMMAND命令
在echo語句中,只有句子分割和儲存空白符的時候,才需要把引數用雙引號引起來。
例子 5-1. 引號引起奇怪的變數
1 #!/bin/bash
2 # weirdvars.sh: Echoing weird variables.
3
4 var="'(]\\{}\$\""
5 echo $var # '(]\{}$"
6 echo "$var" # '(]\{}$" 和上面一句沒什麼不同.
7
8 echo
9
10 IFS='\'
11 echo $var # '(] {}$" \字元被空白符替換了,為什麼?
12 echo "$var" # '(]\{}$"
13
14 exit 0
單引號(' ')和雙引號類似,但它不允許解釋變數引用,因此,在單引號內的字元$的特殊意思無效了。在單引號內,除了字元',每個特殊字元都只是字面的意思。單引號(全域性引用)比雙引號(部分引用)更嚴格的處理引用部分。
由於在單引號裡的轉義字元(\)也只是被侷限於字面上的意思,所以想在一雙單引號裡再加單引號是不行的
1 echo "Why can't I write 's between single quotes"
2
3 echo
4
5 # The roundabout method.
6 echo 'Why can'\''t I write '"'"'s between single quotes'
7 # |-------| |----------| |-----------------------|
8 # 三個單引號引起的字串之間有一個轉義的單引號和一個由雙引號引起的單引號.
注:
[1] 除非當前目錄下有一個檔名為first的檔案。那這是引用的另外一個不同的理由了。
[2] 這也會使變數的值會有副作用。(看下面的)
[3] 在命令列上,把感嘆號"!"放在雙引號裡執行命令會出錯(譯者注:比如說:echo "hello!"). 因為感嘆號被解釋成了一個歷史命令. 然而在一個指令碼檔案裡,這麼寫則是正確的,因為在指令碼檔案裡Bash的歷史機制被禁用了。
在雙號號裡在字元"\"也會引起許多不一致的行為。
bash$ echo hello\!
hello!
bash$ echo "hello\!"
hello\!
bash$ echo -e x\ty
xty
bash$ echo -e "x\ty"
x y
[4] 句子的分割,在這裡是指分割一個字串為許多不連續的單獨的引數.
5.2. 轉義
轉義是引用單字元的方法.在單個字元前面的轉義符(\)告訴shell不必特殊解釋這個字元,只把它當成字面上的意思。
但在一些命令和軟體包裡,比如說echo和sed,轉義一個字元可能會引起一個相反的效果--因為它們可能觸發那個字元的特殊意思。
一些轉義字元的表示的特殊意思
- 和echo,sed連用時:
- \n
-
表示新行
- \r
-
表示回車
- \t
-
表示水平的製表符
- \v
-
表示垂直的製表符
- \b
-
表示後退符
- \a
-
表示“警告”(蜂鳴或是閃動)
- \0xx
-
翻譯成ASCII碼為八進位制0xx所表示的字元
例子 5-2. 轉義字元
1 #!/bin/bash
2 # escaped.sh: 轉義字元
3
4 echo; echo
5
6 echo "\v\v\v\v" # 打印出 \v\v\v\v literally.
7 # 用帶著選項-e的'echo'會打印出轉義字串.
8 echo "============="
9 echo "VERTICAL TABS"
10 echo -e "\v\v\v\v" # 列印四個垂直的製表符.
11 echo "=============="
12
13 echo "QUOTATION MARK"
14 echo -e "\042" # 打印出字元" (引號, 它的八進位制ASCII碼為42).
15 echo "=============="
16
17 # 當使用像$'\X'的結構時,-e選項是多餘的.
18 echo; echo "NEWLINE AND BEEP"
19 echo $'\n' # 新行.
20 echo $'\a' # 警告 (蜂鳴).
21
22 echo "==============="
23 echo "QUOTATION MARKS"
24 # 版本2開始Bash已經允許使用$'\nnn'結構了.
25 # 注意在這裡,'\nnn'表示一個八進位制的值.
26 echo $'\t \042 \t' # Quote (") framed by tabs.
27
28 # 使用$'\xhhh'結構也可以使用十六進位制數來轉義.
29 echo $'\t \x22 \t' # Quote (") framed by tabs.
30 # 多謝Greg Keraunen指出這個..
31 # 早期的Bash版本允許用'\x022'.(譯者注,現在不行了)
32 echo "==============="
33 echo
34
35
36 # 用ASCII碼值把字元賦給變數.
37 # ----------------------------------------
38 quote=$'\042' # 引號"被賦給變數quote了.
39 echo "$quote This is a quoted string, $quote and this lies outside the quotes."
40
41 echo
42
43 # 用連串的ASCII碼把一串字元賦給變數..
44 triple_underline=$'\137\137\137' # 137是字元'_'的ASCII碼.
45 echo "$triple_underline UNDERLINE $triple_underline"
46
47 echo
48
49 ABC=$'\101\102\103\010' # 101, 102, 103分別是A, B, C字元的八進位制ASCII碼.
50 echo $ABC
51
52 echo; echo
53
54 escape=$'\033' # 033是ESC的ASCII碼的八進位制值
55 echo "\"escape\" echoes as $escape"
56 # 不可見的輸出.
57
58 echo; echo
59
60 exit 0
參考擴充套件結構$' '的另外一個例子xample 34-1.
\"
表示引號(")的字面意思
1 echo "Hello" # Hello
2 echo "\"Hello\", he said." # "Hello", he said.
\$
表示美元符($)的字面意思(如果在\$跟上變數名將不會引用變數的值)
echo "\$variable01" # 輸出是$variable01
\\
表示反斜槓(\)的字面意思
1 echo "\\" # 輸出是\
2
3 # 然而 . . .
4
5 echo "\" # 在命令列,這句將會列印SP2變數值(譯者注:變數SP2是輸入未完成提示符),並要求你繼續輸入..
6 # 在指令碼檔案裡, 這句會出錯.
反斜槓的作用要看它是否是自我轉義,被引用,或出現在命令替換結構或是在here document裡.
1 # 簡單的轉義和引用
2 echo \z # z
3 echo \\z # \z
4 echo '\z' # \z
5 echo '\\z' # \\z
6 echo "\z" # \z
7 echo "\\z" # \z
8
9 # 命令替換
10 echo `echo \z` # z
11 echo `echo \\z` # z
12 echo `echo \\\z` # \z
13 echo `echo \\\\z` # \z
14 echo `echo \\\\\\z` # \z
15 echo `echo \\\\\\\z` # \\z
16 echo `echo "\z"` # \z
17 echo `echo "\\z"` # \z
18
19 # Here document
20 cat <<EOF
21 \z
22 EOF # \z
23
24 cat <<EOF
25 \\z
26 EOF # \z
一個字串賦給變數時裡面的組成部分可能會被轉義,但如果單獨一個轉義字元(\)是不能賦給變數的。
1 variable=\
2 echo "$variable"
3 # 不能工作 - 給出一個錯誤資訊:
4 # test.sh: : command not found
5 # 單獨一個轉義字元是不能正確地賦給變數的.
6 #
7 # 那上面語句究竟發生了什麼呢?實際上轉義符"\"轉義了新行符,
8 #+ 產生的作用如同 variable=echo "$variable"
9 #+ 而這是無效的變數賦值
10
11 variable=\
12 23skidoo
13 echo "$variable" # 23skidoo
14 # 這樣就能工作,因為第二行的變數賦值是有效的
15 #
16
17 variable=\
18 # \^ 轉義後面的空格(譯者注:粗心的讀者一定要注意上面最後的空格)
19 echo "$variable" # 空格
20
21 variable=\\
22 echo "$variable" # \
23
24 variable=\\\
25 echo "$variable"
26 # 不能工作 - 產生一個錯誤:
27 # test.sh: \: command not found
28 #
29 # 第一個\轉義第二個\,結果只剩單獨的第三個\字元,
30 #+ 這樣又會發生上面的情況.
31
32 variable=\\\\
33 echo "$variable" # \\
34 # 第二和第四個\字元被轉義.
35 # 這樣不會出錯了.
轉義一個空格可以防止一個字串引數被分割成多個命令列引數。
1 file_list="/bin/cat /bin/gzip /bin/more /usr/bin/less /usr/bin/emacs-20.7"
2 # 檔案列表作為引數傳遞給命令.
3
4 # 再加兩個引數給命令ls,一同列出檔案資訊.
5 ls -l /usr/X11R6/bin/xsetroot /sbin/dump $file_list
6
7 echo "-------------------------------------------------------------------------"
8
9 # 如果我們轉義上面的一對空格會發生什麼?
10 ls -l /usr/X11R6/bin/xsetroot\ /sbin/dump\ $file_list
11 # 出錯: 開頭的三個檔名被連成一個檔名並傳遞給了命令'ls -l'
12 # 因為兩個轉義字元禁止了空格分割引數的作用。
轉義符也提供了寫一個多行命令的手段。一般地,每個單獨的行有一個不同的命令,而在一行末尾的轉義符轉義新行符,命令序列則由下一行繼續。
1 (cd /source/directory && tar cf - . ) | \
2 (cd /dest/directory && tar xpvf -)
3 # 把Alan Cox目錄樹全部複製到另外一個目錄裡,
4 # 但分為兩行可以增加可讀性.
5
6 # 你也可以用下面的命令達到一樣的效果:
7 tar cf - -C /source/directory . |
8 tar xpvf - -C /dest/directory
如果一個指令碼行用一個管道線"|"結束行尾,後面可以再跟一個不必一定要的轉義符"\"。然而,好的程式設計習慣最好加上一個轉義符“\”。
1 echo "foo
2 bar"
3 #foo
4 #bar
5
6 echo
7
8 echo 'foo
9 bar' # 沒什麼不同.
10 #foo
11 #bar
12
13 echo
14
15 echo foo\
16 bar # 新行符被轉義.
17 #foobar
18
19 echo
20
21 echo "foo\
22 bar" # 還是一樣,字元\在弱引用中還是被解釋為轉義字元
23 #foobar
24
25 echo
26
27 echo 'foo\
28 bar' # 由於轉義符"\"在強引用符裡,所以只能解釋為字面上的意思
29 #foo\
30 #bar