Chinaunix首页 | 论坛 | 博客
  • 博客访问: 968991
  • 博文数量: 200
  • 博客积分: 5011
  • 博客等级: 大校
  • 技术积分: 2479
  • 用 户 组: 普通用户
  • 注册时间: 2008-06-27 15:07
文章分类

全部博文(200)

文章存档

2009年(12)

2008年(190)

我的朋友

分类:

2008-07-22 13:02:50

shell中的quoting的使用
shell中可以使用的quoting有三种:
(一)使用'\',backslash字符
    to remove the special meaning from a single character
    A non-quoted backslash ‘\’ is the Bash escape character. It preserves the literal value of the next character that follows, with the exception of newline. If a \newline pair appears, and the backslash itself is not quoted, the \newline is treated as a line continuation (that is, it is removed from the input stream and effectively ignored). 意思看得有点模糊,总之就是用来将后面的字符转义的。

    这里援引一个网中人的shell十三问中第4问关于单引号和双引号区别时所用的一个例子来讲述:

        $ A=B\
        > C\
        >
        $ echo $A
        BC

     可见,此时,backslash并没有被quoting,因此它具有quoting功能,但是由于我们在其后面输入的是回车字符,因此回车字符被当作了line continuation,而且backslash和回车都被忽略了。赫赫。

(二)使用''',单引号
    to inhibit all interpretation of a sequence of characters.
    Enclosing characters in single quotes (‘'’) preserves the literal value of each character within the quotes. A single quote may not occur between single quotes, even when preceded by a backslash.
    就是说单引号可以阻止对它所包含的字符串的任何解释,而且其内部不能再出现'字符,就算用'\'转义不行,echo '\''十一条非法的语句,它提示找不到可以匹配最后一个'的字符。即它将\作为普通字符,将中间的'作为与第一个'匹配的字符,而最后一个'自然就找不到匹配的字符了。

(三)使用'"'双引号
    to suppress most of the interpretation of a sequence of characters
    Enclosing characters in double quotes (‘"’) preserves the literal value of all characters within the quotes, with the exception of ‘$’, ‘`’, ‘\’, and, when history expansion is enabled, ‘!’. The characters ‘$’ and ‘`’ retain their special meaning within double quotes (see ). The backslash retains its special meaning only when followed by one of the following characters: ‘$’, ‘`’, ‘"’, ‘\’, or newline. Within double quotes, backslashes that are followed by one of these characters are removed. Backslashes preceding characters without a special meaning are left unmodified. A double quote may be quoted within double quotes by preceding it with a backslash. If enabled, history expansion will be performed unless an ‘!’ appearing in double quotes is escaped using a backslash. The backslash preceding the ‘!’ is not removed.
    双引号可以禁止对部分字符的解释,但不能禁止对所有特殊字符的解释。
    双引号的使用有些繁琐,有如下规则:
    (1) 双引号中,'$' '`' '!'(如果使能了command history功能)是有特殊功能的字符。'\'当其后面紧跟'$' '`' '\' '"' newline时,是有特殊功能的,其功能是转义,且'\'会被remove.
    (2) 在双引号中,如果'\'后跟着一个没有特殊含义的字符,'\'和该字符都会被保留,不会被remove
    (3) 当command history expansion被开启时,即'!'有作用时,在双引号中,可以用'\'来将'!'转义为普通字符,且'\'被保留不被转义。

    下面是一些echo和quoting的例子,=>后面的内容是在脚本中使用set -ex将命令扩展之后的结果:
    1.1 echo "\n"
    => echo '\n' ('\'后面跟的不是特殊字符,因此'\'也不具有特殊含义,因此保留原样)
    结果输出:\n (2个字符)

    1.2 echo -e "\n"
    => echo -e '\n'
    结果输出:一个空行 ( -e选项会使ANSI C中的使用backslash的扩展其作用,\n对应的就是换行符,当然还有其它许多,可以info echo来看看)

   

    2.1 echo "\\n"
      =>echo '\n' (第一个'\'后跟着一个'\',因此第一个'\'被当作特殊字符,具有转义效果,取后面的'\'字面量,且第一个'\'被remove掉,那么两个'\'的结果就是一个字面量'\',然后跟着一个普通字符n)
    输出结果:\n
   
    2.2 echo -e "\\n"
    =>  echo -e '\n' 原因同2.1
    输出结果:一个空行 原因同1.2



    3.1 echo "\\\n"
    =>  echo '\\n' (第一个'\'后跟一个'\',因此第一个被当作有特殊功能字符,它取后面字符的字面量,而且自己被remove,因此前两个'\'的结果就是一个字面量的'\',第3个'\'后面跟着一个普通字符n,因此'\'和'n'都被保留,最终结果为'\\n')
    输出结果:\\n
   
    3.2 echo -e "\\\n"
    => echo -e '\\n'
    输出结果:\n (没有新行,因为在-e模式下,单引号里,'\'还具有转义效果,第一个'\'将第2个'\'的字面量取出,而且第一个'\'被remove,最后剩下一个'\'字面量,他和普通字符n结合所以结果为\n)
   
    
(四)ANSI C格式的转义字符的使用
    $'string'格式的使用方法:string会被扩展开,而且如果其中含有如下转义序列的话,会被按照转义序列的含义替换:


\a


alert (bell) 响铃


\b


backspace 回退


\e


an escape character 字符 Esc


\f


form feed 进纸


\n


new line 新行符


\r


carriage return 回车


\t


horizontal tab 水平跳格


\v


vertical tab 竖直跳格


\\


backslash 反斜杠


\’


single quote 单引号

                  \nnn          一个八比特字符,它的值是八进制值 nnn (一到三个数字)。

\xHH




HH 一个八比特字符,它的值是十六进制值 HH (一到两个十六进制数字)。


\cx         一个 ctrl-x 字符


扩展结果是单引号引用的,就好像 $ 符号不存在一样。

双引号引用字符串前面加上一个 $ 符号将使得这个字符串被根据当前语言环境 (locale) 来翻译。 如果当前语言环境是 C 或者 POSIX,这个符号将被忽略。 如果这个字符串被翻译并替换了,那么替换结果是双引号引用的。

 
 
    下面转载网中人的关于双引号和单引号的区别:
4) " "(雙引號) 與 ' '(單引號)差在哪?

還是回到我們的 command line 來吧...
經過前面兩章的學習,應該很清楚當你在 shell prompt 後面敲打鍵盤、直到按下 Enter 的時候,
你輸入的文字就是 command line 了,然後 shell 才會以行程的方式執行你所交給它的命令。
但是,你又可知道:你在 command line 輸入的每一個文字,對 shell 來說,是有類別之分的呢?

簡單而言(我不敢說這是精確的定議,註一),command line 的每一個 charactor ,分為如下兩種:
* literal:也就是普通純文字,對 shell 來說沒特殊功能。
* meta:對 shell 來說,具有特定功能的特殊保留字元。
(註一:關於 bash shell 在處理 command line 時的順序說明,
請參考 O'Reilly 出版社之 Learning the Bash Shell, 2nd Edition,第 177 - 180 頁的說明,
尤其是 178 頁的流程圖 Figure 7-1 ... )

Literal 沒甚麼好談的,凡舉 abcd、123456 這些"文字"都是 literal ... (easy?)
但 meta 卻常使我們困惑..... (confused?)
事實上,前兩章我們在 command line 中已碰到兩個機乎每次都會碰到的 meta :
* IFS:由 三者之一組成(我們常用 space )。
* CR:由 產生。
IFS 是用來拆解 command line 的每一個詞(word)用的,因為 shell command line 是按詞來處理的。
而 CR 則是用來結束 command line 用的,這也是為何我們敲 命令就會跑的原因。
除了 IFS 與 CR ,常用的 meta 還有:
= :  設定變量。
$ :  作變量或運算替換(請不要與 shell prompt 搞混了)。
> :重導向 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 quoe 中大部份 meta 都會被關閉,但某些則保留(如 $ )。(註二)
* escape : \ (反斜線),只有緊接在 escape (跳脫字符)之後的單一 meta 才被關閉。
( 註二:在 soft quote 中被豁免的具體 meta 清單,我不完全知道,
有待大家補充,或透過實作來發現及理解。 )

下面的例子將有助於我們對 quoting 的了解:

CODE:
        $ A=B C        # 空白鍵未被關掉,作為 IFS 處理。
        $ C: command not found.
        $ echo $A
       
        $ A="B C"        # 空白鍵已被關掉,僅作為空白鍵處理。
        $ echo $A
        B C

在第一次設定 A 變量時,由於空白鍵沒被關閉,command line 將被解讀為:
* A=B 然後碰到,再執行 C 命令
在第二次設定  A 變量時,由於空白鍵被置於 soft quote 中,因此被關閉,不再作為 IFS :
* A=BC
事實上,空白鍵無論在 soft quote 還是在 hard quote 中,均會被關閉。Enter 鍵亦然:

CODE:
        $ A='B
        > C
        > '
        $ echo "$A"
        B
        C

在上例中,由於 被置於 hard quote 當中,因此不再作為 CR 字符來處理。
這裡的 單純只是一個斷行符號(new-line)而已,由於 command line 並沒得到 CR 字符,
因此進入第二個 shell prompt (PS2,以 > 符號表示),command line 並不會結束,
直到第三行,我們輸入的 並不在  hard quote 裡面,因此並沒被關閉,
此時,command line 碰到 CR 字符,於是結束、交給 shell 來處理。

上例的 要是被置於 soft quote 中的話, CR 也會同樣被關閉:

CODE:
        $ A="B
        > C
        > "
        $ echo $A
        B C

然而,由於 echo $A 時的變量沒至於 soft quote 中,因此當變量替換完成後並作命令行重組時, 會被解釋為 IFS ,而不是解釋為 New Line 字符。

同樣的,用 escape 亦可關閉 CR 字符:

CODE:
        $ A=B\
        > C\
        >
        $ echo $A
        BC

上例中,第一個 跟第二個 均被 escape 字符關閉了,因此也不作為 CR 來處理,
但第三個 由於沒被跳脫,因此作為 CR 結束 command line 。
但由於 鍵本身在 shell meta 中的特殊性,在 \ 跳脫後面,僅僅取消其 CR 功能,而不會保留其 IFS 功能。

您或許發現光是一個 鍵所產生的字符就有可能是如下這些可能:
CR
IFS
NL(New Line)
FF(Form Feed)
NULL
...
至於甚麼時候會解釋為甚麼字符,這個我就沒去深挖了,或是留給讀者諸君自行慢慢摸索了... ^_^

至於 soft quote 跟 hard quote 的不同,主要是對於某些 meta 的關閉與否,以 $ 來作說明:

CODE:
        $ A=B\ C
        $ echo "$A"
        B C
        $ echo '$A'
        $A

在第一個 echo 命令行中,$ 被置於 soft quote 中,將不被關閉,因此繼續處理變量替換,
因此 echo 將 A 的變量值輸出到熒幕,也就得到  "B C" 的結果。
在第二個 echo 命令行中,$ 被置於 hard quote 中,則被關閉,因此 $ 只是一個 $ 符號,
並不會用來作變量替換處理,因此結果是 $ 符號後面接一個 A 字母:$A 。

--------------------------------------
練習與思考:如下結果為何不同?

CODE:
        $ A=B\ C
        $ echo '"$A"'        # 最外面的是單引號
        "$A"
        $ echo "'$A'"        # 最外面的是雙引號
        'B C'
        (提示:單引號及雙引號,在 quoting 中均被關?#93;了。)

--------------------------------------

在 CU 的 shell 版裡,我發現有很多初學者的問題,都與 quoting 理解的有關。
比方說,若我們在 awk 或 sed 的命令參數中調用之前設定的一些變量時,常會問及為何不能的問題。
要解決這些問題,關鍵點就是:
* 區分出 shell meta 與 command meta

前面我們提到的那些 meta ,都是在 command line 中有特殊用途的,
比方說 { } 是將其內一系列 command line 置於不具名的函式中執行(可簡單視為 command block ),
但是,awk 卻需要用 { } 來區分出 awk 的命令區段(BEGIN, MAIN, END)。
若你在 command line 中如此輸入:

CODE:
$ awk {print $0} 1.txt

由於  { } 在 shell 中並沒關閉,那 shell 就將 {print $0} 視為 command block ,
但同時又沒有" ; "符號作命令區隔,因此就出現 awk 的語法錯誤結果。

要解決之,可用 hard quote :

CODE:
$ awk '{print $0}' 1.txt

上面的 hard quote 應好理解,就是將原本的 {、、$(註三)、} 這幾個 shell meta 關閉,
避免掉在 shell 中遭到處理,而完整的成為 awk 參數中的 command meta 。
( 註三:而其中的 $0 是 awk 內建的 field number ,而非  awk 的變量,
awk 自身的變量無需使用 $ 。)
要是理解了 hard quote 的功能,再來理解 soft quote 與 escape 就不難:

CODE:
awk "{print \$0}" 1.txt
awk \{print\ \$0\} 1.txt

然而,若你要改變 awk 的 $0 的 0 值是從另一個 shell 變量讀進呢?
比方說:已有變量 $A 的值是 0 ,那如何在 command line 中解決 awk 的 $$A 呢?
你可以很直接否定掉 hard quoe 的方案:

CODE:
$ awk '{print $$A}' 1.txt

那是因為 $A 的 $ 在 hard quote 中是不能替換變量的。

聰明的讀者(如你!),經過本章學習,我想,應該可以解釋為何我們可以使用如下操作了吧:

CODE:
A=0
awk "{print \$$A}" 1.txt
awk \{print\ \$$A\} 1.txt
awk '{print $'$A'}' 1.txt
awk '{print $'"$A"'}' 1.txt     # 注:"$A" 包在 soft quote 中

或許,你能舉出更多的方案呢....  ^_^

--------------------------------------
阅读(1104) | 评论(0) | 转发(0) |
0

上一篇:awk脚本的调用

下一篇:bash中的directory stack

给主人留下些什么吧!~~