2012-04-03 31 views
3

我在編寫bash腳本時經常被引用和評估規則困惑。我知道一些基本知識,比如說''和'「和」`「之間的區別,但我似乎仍然錯誤得太多,並且試圖用各種不同的方式來說同一件事。如何學習如何在bash中引用權限?

任何個人問題我通常可以通過暴力來解決,但我認爲我的概念模型是如何運作的,必須以某種未知的方式被無望地破壞。

我對lisp的報價,評估,閱讀,打印,語法引用系統沒有問題。事實上,我寫了一個小卡塔來幫助人們瞭解發生了什麼:http://www.learningclojure.com/2010/11/syntax-quote-kata-for-confused.html

我想我正在尋找類似於bash的東西(這看起來要複雜得多)。一個好的模型或一組練習可以幫助我形成這樣的模型,這將使​​我能夠查看一個複雜的shell腳本,其中變量被轉換和評估,打印出來並閱讀,並計算出將要發生的事情而不必嘗試。

如果沒有,那麼調試過程的好方法,以及在評估的每個階段看看發生了什麼都會很有幫助。

+2

閱讀[手冊](http://www.gnu.org/software/bash/manual/bash.html#Quoting) – kev 2012-04-03 13:39:45

+3

避免反引號並使用'$()'來代替...,這會刪除很多引用的考慮已經... – 0xC0000022L 2012-04-03 13:43:32

回答

9

Bruce Barnett's UNIX Shell Quote Tutorial很棒,而Bash FAQ/pitfalls/word splitting文章有很多有用的提示。簡短的總結:

未加引用字符串可以包含大多數字符,但不是全部(如換行符),其中許多字符(包括空格)將不得不轉義。只是不要使用它們 - 如果你陷入誘惑,你可能會發現修改腳本的人在需要時忘記包含引號。

單引號字符串可以包含大部分字符,包括NUL和換行符,但不是單引號,所以他們也是有用的,只有簡單的值。

反引號是用於命令。只有當你的shell不支持$()時才應該使用它們。例如:

current_dir=`pwd` # BAD! Don't do this! 

該命令是不好的,因爲當分配的右手側不援引殼上執行它word splitting。它通常會導致難以複製的錯誤,因爲空白難以通過視覺檢查。爲了報價命令使用雙引號

current_dir="$(pwd)" # OK, but loses newlines at EOF 

換行符在EOF尤其棘手。您可以添加一個字符,並通過使用例如

# Works for some commands, but not pwd 
current_dirx="$(pwd; echo x)" 
current_dir="${current_dirx%x}" 
printf %s "$current_dir" 

剝離,但有一個額外的困難,因爲一些命令(如pwd)將在其輸出端反正的末尾添加一個新行,所以你可能必須消除,以及:

# Works for some commands, including pwd 
current_dirx="$(pwd; echo x)" 
current_dir="${current_dirx%$'\nx'}" 
printf %s "$current_dir" 

雙引號可以包含任何字符(嘗試echo -ne "\0" | wc -c),但要注意變量不能包含NULL字符。

ANSI-C quotes可以包含任意字符除了NUL(嘗試echo -ne $'\0' | wc -c),並提供方便的轉義代碼,使其更容易與特殊字符的工作:

printf %s $'--$`!*@\a\b\E\f\r\t\v\\\'"\360\240\202\211 \n' 
printf %q $'--$`!*@\a\b\E\f\r\t\v\\\'"\360\240\202\211 \n' 
touch -- $'--$`!*@\a\b\E\f\r\t\v\\\'"\360\240\202\211 \n' 
rm -- $'--$`!*@\a\b\E\f\r\t\v\\\'"\360\240\202\211 \n' 
1

使用singlequotes ''用於引用原始文本(即使是反斜槓也不能在單引號中逃脫任何東西):

> echo '\' 
\ 
> echo '$PATH' 
$PATH 

使用雙引號""用於引用文本,該文本包含shell應像變量($bla),子shell調用($(ls))和評估($((5 + 3)))一樣評估的內容。

> echo "$PATH" 
/home/alfe/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games 
> echo "$(ls | tail -1)" 
bla 
> echo "$((5 + 3))" 
8 

使用反引號``如果由於某種原因,你不能使用$()(例如,你必須向你sh而不是bash,通常使用$()到子shell的輸出聚集成當前命令的罕見病例。

> echo "$(ls | tail -1) is the last file in the current dir." 
bla is the last file in the current dir. 

一個我碰到與其他人的慶典代碼的主要問題是缺少周圍的東西,往往就是一個字,但在極少數情況下,雙引號可能不止一個字,或可以包含特殊字符。因此,使用雙引號whe他們是可能的。

> a="four spaces" 
> echo $a 
four spaces 
> echo "$a" 
four spaces 
+1

請注意,舊格式$ [表達式]已被棄用,並將在即將到來的版本的bash中被刪除。「 (從'man bash') - 使用'$((表達式))'代替。 – l0b0 2012-04-12 11:07:31

+0

改變了這一點,謝謝提醒。 – Alfe 2012-04-12 22:17:34

0

在shell提示,

set -x 
set -v 

並創建以下Python程序 'args.py'

#!/usr/bin/env python 

import sys 
print sys.argv 
for arg in sys.argv[1:]: 
    for c in arg: 
     print c,"|", 
    print 

然後用命令調用等實驗:

U=hello\ world ; V="-->$U<--"; W="1 $U 2 $V 3"; args.py $W 

直到你意識到了那裏沒有邏輯的思維方式正在發生什麼。這一切都是由反覆無常的魔法小精靈完成的。

+0

發生了什麼是[word splitting](http://mywiki.wooledge.org/WordSplitting) - 您只需使用引號和/或數組將一組變量作爲參數傳遞給shell腳本:'U = hello \世界; V = 「 - > $ U < - 」; W =(「1」「$ U」「2」「$ V」「3」); args.py「$ {W [@]}」' – l0b0 2012-04-12 11:02:47