2013-03-16 89 views
5

在目錄~/temp/a/foo/~/temp/b/foo foo/我有一些名爲bar1bar2bar bar1bar bar2雙引號VS星號文件名擴展中的Bash

我試圖寫一行猛砸,所有副本的這些文件的文件在包含「foo」作爲名稱的最後部分的目錄中,該目錄位於相應「foo」文件夾上方的文件夾中。

只要有在文件名中沒有空格,這是一件容易的事,但隨着foo foo目錄打交道時,以下兩個命令失敗:

for dir in `find . -type d -name '*foo'` ; do cp $dir/* "$(echo $dir|sed 's_foo__g')" ; done 

(該cp命令無法看到的"foo foo"最後foo爲相同的目錄名稱的一部分。)

for dir in `find . -type d -name '*foo'` ; do cp "$dir/*" "$(echo $dir|sed 's_foo__g')" ; done 

"$dir/*"沒有展開。)

嘗試替換$dir/*"$(echo $dir/*)"更不成功。

是否有一種簡單的方法來擴展$dir/*以便cp能夠理解?

+0

請參閱http://mywiki.wooledge.org/DontReadLinesWithFor – 2013-03-16 23:34:27

回答

4

不僅for環路錯誤 - sed也不適合此工作。

while IFS= read -r -d '' dir; do 
    cp "$dir" "${dir/foo/}" 
done < <(find . -type d -name '*foo' -print0) 

find(和IFS= read -r -d '')使用-print0確保與新行的文件名不會弄亂你。

使用< <(...)構造可確保如果循環內部設置變量,更改目錄狀態或做類似的事情,則這些更改將會存活(管道的右側位於bash中的子shell中,因此管道進入while循環將意味着在while循環中對shell的狀態所做的任何更改都會在其退出時被丟棄)。

使用${dir/foo/}比調用sed更有效,因爲它在bash內部進行字符串替換。

+0

你確定需要'IFS ='嗎?偉大的職位btw! – janos 2013-03-16 23:41:04

+0

儘管janos建議接受這個答案作爲解決方案,但事實證明,代碼實際上並沒有移動任何文件。 Bash 4.2.24抱怨cp不能統計''。我不是100%確定'cp「$ dir」「$ {dir/foo /}」'這行是幹什麼的。它真的試圖將文件複製到'foo'目錄上的現有目錄嗎? – uvett 2013-03-17 00:13:25

+0

不應該'讀取...文件'是'read ... dir'? – 2013-03-17 00:14:54

0

~/temp/b/foo foo/重命名爲沒有空格的內容,例如~/temp/b/foo_foo/並做你正在嘗試做的事。之後,如果您真的需要,可以使用空格將其重命名爲原始文件。 BTW。我自己從來沒有使用含有空格的文件名,只是爲了避免像現在面臨的那樣複雜。

+0

我試圖簡化一個涉及數十個文件夾和數百個帶有難看名字的文件的問題。重命名它們看起來像是次優選項,特別是因爲我將不得不稍後恢復文件夾名稱。 – uvett 2013-03-16 23:18:08

2

這裏的問題不是cp,而是for,因爲默認情況下,它會通過單詞而不是目錄名來分割subshel​​l的輸出。

懶惰的解決方法是通過線使用while而不是處理目錄線名單如下:

find . -type d -name '*foo' | while read dir; do cp "$dir"/* "$(echo $dir | sed 's_foo__g')" ; done 

這應該與空間解決您的問題,但是這絕不是一個萬無一失的解決方案。

有關更準確的解決方案,請參閱Charles Duffy的答案。

+0

幾乎是正確的,但是您確實應該NUL分隔管道的內容 - 否則,包含換行符的惡意製作的文件名可能導致任意文件被複制。 – 2013-03-16 23:35:01

+0

...順便說一下,'dir'永遠不會包含空格作爲'for'工作方式的工件;單詞拆分不是for語義的一部分,如果'IFS'被設置爲不包含空格,則string-split輸出_can_包含空格。也就是說,依賴於字符串分割的原因是錯誤的(例如,因爲glob擴展同時發生)。 – 2013-03-16 23:40:11

+0

謝謝,這似乎適用於我所有的真實世界的文件複製問題。它在我簡化的例子中實際上失敗了,是一個有趣的細節。 (sed刪除'foo foo'文件夾名稱中的'foo')。 – uvett 2013-03-16 23:45:56