2015-03-03 82 views
0

在Mac OS X,我有一個bash腳本是這樣的:爲什麼grep忽略包含要忽略的目錄的shell變量?

# Directories excluded from grep go here. 
EXCLUDEDIR="--exclude-dir={node_modules,.git,tmp,angular*,icons,server,coffee}" 

# This grep needs to include one line below the hit. 
grep -iIrn -A1 $EXCLUDEDIR -e "class=[\"\']title[\"\']>$" -e "<div class=\"content" . > microcopy.txt 

,但它似乎被忽略$EXCLUDEDIR。如果我直接使用--exclude-dir,它可以工作。爲什麼它不能擴大變量並正確工作?

+0

你有沒有名字以'angular'開頭,並且裏面有空格的目錄? – 2015-03-03 04:33:31

+0

@KyleStrand我沒有任何帶空格的目錄名稱;我懷疑原因如下由tripleee給出。 – user766353 2015-03-03 16:21:11

+0

啊。是的,那會做。 – 2015-03-03 17:14:04

回答

5

大括號在技術上是一個錯誤。當它們在一個變量中時,它們被逐字包含,而當你直接將它們作爲命令的一部分鍵入時,Bash執行大括號擴展,並有效地從表達式中刪除大括號。

bash$ echo --exclude-dir=moo{bar,baz} 
--exclude-dir=moobar --exclude-dir=moobaz 

bash$ x='moo{bar,baz}' 
bash$ echo --exclude-dir=$x 
--exclude-dir=moo{bar,baz} 

(不是那麼簡單)的解決方法是顯式地列出你的參數。這可以通過使用數組來列出要排除的目錄名稱(但不能移植到傳統的/bin/sh)來簡化。

x=(node_modules .git tmp angular\* icons server coffee) 
EXCLUDEDIR="${x[@]/#/--exclude-dir=}" 

angular\*反斜槓是通過對grep未擴展到通過這個通配符表達式 - 如果外殼將擴大變量,grep也不排除目錄匹配子目錄中的通配符表達式(除非他們方便正好匹配一個當前目錄中的擴展值)。如果您有nullglob有效,則非轉義通配符會從列表中消失。

+0

如果他打算排除名稱以「angular」開頭的所有目錄,則不應引用該令牌(單詞),也不應將星號反斜槓轉義。 – nawK 2015-03-03 07:13:57

+0

@klf取決於。在分配數組時,shell會將未加引號的'angular *'擴展到當前目錄中的任何和所有匹配項。如果沒有,並且'nullglob'沒有生效,它將留下一個星號。如果'nullglob'有效,它將被刪除,除非在本地目錄中有匹配,在這種情況下它們將被擴展;但是,子目錄中的任何匹配都不會包含在內。所以如果你有'angular1'和'subdir/angular2',那麼在這種情況下'subdir/angular2'不會被排除。 – tripleee 2015-03-03 08:21:55

+0

如果在這種情況下變量不起作用,那麼可能明確列出參數是最簡單和最安全的方法。 – user766353 2015-03-03 16:36:34

0

@tripleee正確地描述該問題,但也有我認爲是簡單(我想,更便攜的)兩種解決方法不是使用一個磁盤陣列:使用evalgit命令,或在可變分配本身使用echoecho方法是優選的。

使用eval

# Directories excluded from grep go here. 
EXCLUDEDIR="--exclude-dir={node_modules,.git,tmp,angular*,icons,server,coffee}" 

# This grep needs to include one line below the hit. 
eval grep -iIrn -A1 $EXCLUDEDIR # .... etc 

這會導致因爲如果他們確確實實輸入括號進行擴展。但請注意,如果您不小心,可能會產生一些意想不到的副作用;例如,您可能需要添加一些額外的\以避開引號和$-符號。

使用echo

這可能比eval更安全,因爲你不會意外地執行代碼隱藏在EXCLUDEDIR變量。

# Directories excluded from grep go here. 
EXCLUDEDIR="$(echo --exclude-dir={node_modules,.git,tmp,angular*,icons,server,coffee})" 

# This grep needs to include one line below the hit. 
grep -iIrn -A1 $EXCLUDEDIR # .... etc 
+0

對eval及其對該命令中其他任何內容的影響(或者可能以該命令結束)沒有深入的瞭解,我猶豫不決。我對於可能出現的不良後果缺乏足夠的理解。 – user766353 2015-03-03 22:01:52

+0

夠公平的。 ''eval'基本上只是增加了Bash在將命令傳遞給'grep'之前執行的第二次插值迭代。我認爲你可能碰到的主要問題是,在第一次評估之後,引用的標記現在沒有被引用,所以你可能必須在內部參數中使用'$'來轉義或者加引號。不過,你可以簡單地用'eval echo grep'來玩弄它。我認爲這個用法是安全的,因爲你控制了'EXCLUDEDIR'變量,並且在'eval'之前立即設置它。 – 2015-03-04 00:25:25

+0

@ user766353我添加了一個使用'$()'和'echo'而不是'eval'的解決方案 – 2015-03-04 00:27:45