免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: 網中人
打印 上一主题 下一主题

[学习共享] shell 十三問? [复制链接]

论坛徽章:
0
31 [报告]
发表于 2003-12-19 10:32 |只看该作者

shell 十三問?

好东西

论坛徽章:
0
32 [报告]
发表于 2003-12-21 09:08 |只看该作者

shell 十三問?

期待ing

论坛徽章:
0
33 [报告]
发表于 2003-12-21 22:15 |只看该作者

shell 十三問?

等待ing.

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
34 [报告]
发表于 2003-12-22 03:31 |只看该作者

shell 十三問?

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> 或 <tab> 或 <enter> 三者之一組成(我們常用 space )。
* CR:由 <enter> 產生。
IFS 是用來拆解 command line 的每一個詞(word)用的,因為 shell command line 是按詞來處理的。
而 CR 則是用來結束 command line 用的,這也是為何我們敲 <enter> 命令就會跑的原因。
除了 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 的了解:
  1.         $ A=B C        # 空白鍵未被關掉,作為 IFS 處理。
  2.         $ C: command not found.
  3.         $ echo $A
  4.        
  5.         $ A="B C"        # 空白鍵已被關掉,僅作為空白鍵處理。
  6.         $ echo $A
  7.         B C
复制代码

在第一次設定 A 變量時,由於空白鍵沒被關閉,command line 將被解讀為:
* A=B 然後碰到<IFS>,再執行 C 命令
在第二次設定  A 變量時,由於空白鍵被置於 soft quote 中,因此被關閉,不再作為 IFS :
* A=B<space>C
事實上,空白鍵無論在 soft quote 還是在 hard quote 中,均會被關閉。Enter 鍵亦然:
  1.         $ A='B
  2.         > C
  3.         > '
  4.         $ echo "$A"
  5.         B
  6.         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 也會同樣被關閉:
  1.         $ A="B
  2.         > C
  3.         > "
  4.         $ echo $A
  5.         B C
复制代码

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

同樣的,用 escape 亦可關閉 CR 字符:
  1.         $ A=B\
  2.         > C\
  3.         >
  4.         $ echo $A
  5.         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 的關閉與否,以 $ 來作說明:
  1.         $ A=B\ C
  2.         $ echo "$A"
  3.         B C
  4.         $ echo '$A'
  5.         $A
复制代码

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

--------------------------------------
練習與思考:如下結果為何不同?
  1.         $ A=B\ C
  2.         $ echo '"$A"'        # 最外面的是單引號
  3.         "$A"
  4.         $ echo "'$A'"        # 最外面的是雙引號
  5.         'B C'
  6.         (提示:單引號及雙引號,在 quoting 中均被關閉了。)
复制代码

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

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

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

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

要解決之,可用 hard quote :
  1. $ awk '{print $0}' 1.txt
复制代码

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


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

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

聰明的讀者(如你!),經過本章學習,我想,應該可以解釋為何我們可以使用如下操作了吧:
  1. A=0
  2. awk "{print \$$A}" 1.txt
  3. awk \{print\ \$$A\} 1.txt
  4. awk '{print $'$A'}' 1.txt
  5. awk '{print $'"$A"'}' 1.txt     # 注:"$A" 包在 soft quote 中
复制代码


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

--------------------------------------
練習與思考:請運用本章學到的知識分析如下兩串討論:
http://bbs.chinaunix.net/forum/viewtopic.php?t=207178
http://bbs.chinaunix.net/forum/viewtopic.php?t=216729
--------------------------------------

[ 本帖最后由 網中人 于 2008-10-30 02:07 编辑 ]

论坛徽章:
0
35 [报告]
发表于 2003-12-22 11:26 |只看该作者

shell 十三問?

awk '{print $'$A'}' 1.txt
awk '{print $'"$A"'}' 1.txt     # 注:"$A" 包在 soft quote 中
请问网中人兄:
   这两行怎么解释执行的?

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
36 [报告]
发表于 2003-12-22 14:36 |只看该作者

shell 十三問?

你只要抓到並解讀出如下這兩段 hard quote 就能理解了:
'{print $'
'}'

论坛徽章:
0
37 [报告]
发表于 2003-12-22 15:03 |只看该作者

shell 十三問?

网中人兄:
对呀 ,我研究了这篇文章一上午,中午吃了饭就来看,整整看了五面,
明白了,把第一个和第二个'看成一起后面两个看成一起.
谢谢!!

论坛徽章:
0
38 [报告]
发表于 2003-12-22 16:01 |只看该作者

shell 十三問?

IFS=;
看了很多贴子,感觉IFS的概念有点模糊,理解能力差了.

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
39 [报告]
发表于 2003-12-22 16:11 |只看该作者

shell 十三問?

上面的 IFS=; 其實就是:
IFS=
將值設為 null
然後補一個" ; " 的 meta ...

昨晚的文章,一開始就提到 IFS 的作用了:
是 breaks command line down to each word 用的...
而 command line 則是 processes word by word 進行的,
若碰到 meta 或 key words ,會先處理之,等到所有的 words 都處理完畢,
那才開始傳給 shell 處理...

man bash 在 Special Parameters 那節有提到:
  1.        *      Expands to the positional parameters, starting from
  2.               one.   When  the  expansion  occurs  within  double
  3.               quotes,  it expands to a single word with the value
  4.               of each parameter separated by the first  character
  5.               of  the  IFS  special  variable.   That is, "$*" is
  6.               equivalent to "$1c$2c...", where  c  is  the  first
  7.               character of the value of the IFS variable.  If IFS
  8.               is unset, the parameters are separated  by  spaces.
  9.               If  IFS  is null, the parameters are joined without
  10.               intervening separators.
复制代码


請理解最後一句的意思,你就能理解 IFS= 的意思...

论坛徽章:
0
40 [报告]
发表于 2003-12-23 10:46 |只看该作者

shell 十三問?

#cat 1.txt
    1      2
    3      4
#cat 1.txt|while read i;do echo "$i";done
1      2
3      4
也就是说,前面的空字符没有了?用IFS=;的大概意思就是用空字符来分段,对否?
#a="   abc"
#echo "$a"
    abc         #能够正常输出前面的空字符。
环境:sco 5.04
    
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP