command line的每一個charactor分爲如下兩種:
- literal:也就是普通純文字,對shell來說沒有特殊功能。
- meta:對shell來說,具有特定功能的保留字。
- literal,凡是 abcd、123456 等這些“文字”都是literal。
- meta:
IFS:由< space> < tab> < enter>三者之一組成(我們常用space)。
CR:由< enter>產生。用來結束command line用的,這也是爲何我們敲< enter>命令就會執行的原因。
除了IFS和CR外,常用的meta還有:
= : 設定變量。
$ : 做變量或運算替換
>: 重定向 stdout。
< : 重定向 stdin。
|: 管道命令。
& : 重定向 file descriptor ,或將命令置於後臺執行。
( ): 將其內的命令置於 nested subshell 執行,或用於運算或命令替換。
{ }: 將其內的命令置於 non-named function 中執行,或用在變量替換的界定範圍。
; : 在前一個命令結束時,而忽略其返回值,繼續執行下一個命令。
&& : 在前一個命令結束時,若返回值爲 true,繼續執行下一個命令。
|| : 在前一個命令結束時,若返回值爲 false,繼續執行下一個命令。
!: 執行 history 列表中的命令
….
假如我們要在command line中將這些保留元字符的功能關閉的話,就要用到 quoting 處理了。
在bash中,我們常用的 quoting有如下三種方法:
*hard quote:”(單引號),凡在hard quote中的所有meta均被關閉。
*soft quote:”“(雙引號),在soft quote中的大部分meta都會被關閉,但某些保留(如$)。
*escape:\ (反斜線),只有緊接在escape(跳脫字符)之後的單一meta才被關閉。
下面的例子將有助於我們對 quoting 的瞭解:
$ A=B C # 空白鍵未被關閉,作爲IFS 處理。
$ C: command not found.
$ echo $A
$ A="B C" # 空白鍵已被關閉,僅作空白符號處理。
$ echo $A
B C
在第一次設定 A 變量時,由於空白鍵沒有被關閉,command line 將被解讀爲:
A=B 然後碰到< IFS>,再執行 C 命令
在第二次設定 A 變量時,由於空白鍵置於 soft quote 中,因此被關閉,不再作爲 IFS :
A=B< space>C
事實上,空白鍵無論在 soft quote 還是在 hard quote 中,均會被關閉。Enter 鍵亦然
$ A='B
> C
> '
$ echo "$A"
B
C
- 在上例中,由於 < enter> 被置於 hard quote 當中,因此不再作爲 CR 字符來處理。 這裏的 < enter>單純只是一個斷行符號(new-line)而已,由於 command line 並沒得到 CR 字符,因此進入第二個 shell prompt (PS2,以 > 符號表示),command line 並不會結束,直到第三行,我們輸入的 < enter> 並不在 hard quote 裏面,因此並沒被關閉,此時,command line 碰到 CR 字符,於是結束、交給 shell 來處理。
上例的 < enter> 要是被置於 soft quote 中的話, CR 也會同樣被關閉:
$ A="B
> C
> "
$ echo $A
B C
然而,由於 echo $A 時的變量沒置於 soft quote 中,因此當變量替換完成後並作命令行重組時,< enter> 會被解釋爲 IFS ,而不是解釋爲 New Line 字符。
同樣的,用 escape 亦可關閉 CR 字符:
$ A=B\
> C\
>
$ echo $A
BC
上例中,第一個 < enter> 跟第二個 < enter> 均被 escape 字符關閉了,因此也不作爲 CR 來處理,
但第三個 < enter> 由於沒有被跳脫,因此作爲 CR 結束 command line 。
但由於 < enter> 鍵本身在 shell meta 中的特殊性,在 \ 跳脫後面,僅僅取消其 CR 功能,而不會保留其 IFS 功能。
您或許發現光是一個 < enter> 鍵所產生的字符就有可能是如下這些可能:
CR
IFS
NL(New Line)
FF(Form Feed)
NULL
…
至於 soft quote 跟 hard quote 的不同,主要是對於某些 meta 的關閉與否,以 $ 來作說明:
$ A=B\ C
$ echo "$A"
B C
$ echo '$A'
$A
在第一個 echo 命令行中,
並不會用來作變量替換處理,因此結果是
如下結果:
$ A=B\ C
$ echo '"$A"' # 最外面的是單引號
"$A"
$ echo "'$A'" # 最外面的是雙引號
'B C'
在 CU 的 shell 版裏,我發現有很多初學者的問題,都與 quoting 理解的有關。
比方說,若我們在 awk 或 sed 的命令參數中調用之前設定的一些變量時,常會問及爲何不能的問題。
要解決這些問題,關鍵點就是:
* 區分出 shell meta 與 command meta*
前面我們提到的那些 meta ,都是在 command line 中有特殊用途的,
比方說 { } 是將其內一系列 command line 置於不具名的程序中執行(可簡單視爲 command block ),
但是,awk 卻需要用 { } 來區分出 awk 的命令區段(BEGIN, MAIN, END)。
若你在 command line 中如此輸入:
$ awk {print $0} 1.txt
由於 { } 在 shell 中並沒關閉,那 shell 就將 {print $0} 視爲 command block ,
但同時又沒有” ; “符號作命令區隔,因此就出現 awk 的語法錯誤結果。
要解決之,可用 hard quote :
$ awk '{print $0}' 1.txt
上面的 hard quote 應好理解,就是將原本的 {、< space>、$(注.)、} 這幾個 shell meta 關閉,
避免掉在 shell 中遭到處理,而完整的成爲 awk 參數中的 command meta 。
- ( 注.:而其中的
0是awk內建的fieldnumber,而非awk的變量,awk自身的變量無需使用 。)
要是理解了 hard quote 的功能,再來理解 soft quote 與 escape 就不難:
awk "{print \$0}" 1.txt
awk \{print\ \$0\} 1.txt
然而,若你要改變 awk 的
你可以很直接否定掉 hard quoe 的方案:
$ awk '{print $$A}' 1.txt
那是因爲
單引號和雙引號都能關閉shell對特殊字符的處理。不同的是,雙引號沒有單引號嚴格,單引號關閉所有有特殊作用的字符,而雙引號只要求shell忽略大多數,具體的說,就是①美元符號②反引號③反斜槓,這3種特殊字符不被忽略。 不忽略美元符號意味着shell在雙引號內部也進行變量名替換。