使用 xargs 命令

 xargs 命令讀取標準的輸入,然後使用參數作爲輸入構建和執行命令。如果沒有給出命令,那麼將使用 echo 命令。清單 12 是使用我們的 text1 文件的基礎例子,它包含 3 個行,每行只有兩個單詞。

清單 12. 使用 xargs


				
[ian@echidna lpi103-4]$ cat text1
1 apple
2 pear
3 banana
[ian@echidna lpi103-4]$ xargs<text1
1 apple 2 pear 3 banana

 

爲什麼 xargs 只有一行輸出?默認情況下,xargs 在空格處中斷輸出,並且每個生成的標記都成爲一個參數。不過,當 xargs 構建命令時,它將一次傳遞儘可能多的參數。您可以使用 -n 覆蓋該行爲,或使用 --max-args 參數。在清單 13 中,我們使用了這兩種方法,併爲使用xargs 添加一個顯式的 echo 調用。

清單 13. 使用 xargs 和 echo


				
[ian@echidna lpi103-4]$ xargs<text1 echo "args >"
args > 1 apple 2 pear 3 banana
[ian@echidna lpi103-4]$ xargs --max-args 3 <text1 echo "args >"
args > 1 apple 2
args > pear 3 banana
[ian@echidna lpi103-4]$ xargs -n 1 <text1 echo "args >"
args > 1
args > apple
args > 2
args > pear
args > 3
args > banana

 

如果輸入包含由單引號或雙引號保護的空格,或使用了斜槓進行轉義,那麼 xargs 將不在遇到這些空格時中斷。清單 14 顯示了這些空格點。

清單 14. 使用帶引號的 xargs


				
[ian@echidna lpi103-4]$ echo '"4 plum"' | cat text1 -
1 apple
2 pear
3 banana
"4 plum"
[ian@echidna lpi103-4]$ echo '"4 plum"' | cat text1 - | xargs -n 1
1
apple
2
pear
3
banana
4 plum

 

到目前爲止,已經在命令的末尾添加了所有參數。如果您需要在這些參數後面再使用其他參數,可以使用 -I 選項指定一個替換字符串。如果 xargs 將要執行的命令包含有替換字符串,那麼將使用參數替換它。進行了替換之後,僅將參數傳遞給每個命令。不過,將從一整行輸出創建參數,而不僅是一個標記。您 還可以使用 xargs 的 -L 選項讓命令將行當作參數看待,而不是默認的以單個空格分隔的標記。使用 -I 選項表示 -L 1。 清單 15 顯示了使用 -I 和 -L 選項的例子。

清單 15. 使用帶有輸入行的 xargs


				
[ian@echidna lpi103-4]$ xargs -I XYZ echo "START XYZ REPEAT XYZ END" <text1
START 1 apple REPEAT 1 apple END
START 2 pear REPEAT 2 pear END
START 3 banana REPEAT 3 banana END
[ian@echidna lpi103-4]$ xargs -IX echo "<X><X>" <text2
<9 plum><9 plum>
<3 banana><3 banana>
<10 apple><10 apple>
[ian@echidna lpi103-4]$ cat text1 text2 | xargs -L2
1 apple 2 pear
3 banana 9 plum
3 banana 10 apple

 

儘管我們的例子爲了便於演示使用了簡單的文本文件,您很少看到包含這樣的輸入的 xargs。您通常需要處理某些 命令生成的大量文件,這些命令包括 lsfind 或 grep。清 單 16 顯示了一種通過 xargs 將目錄清單傳遞到命令(比如 grep)的方法。

清單 16. 使用帶有多個文件的 xargs


				
[ian@echidna lpi103-4]$ ls |xargs grep "1"
text1:1 apple
text2:10 apple
xaa:1 apple
yaa:1

 

如果上一個例子中的一個或多個文件名包含空格,那麼會發生什麼呢?如果您像清單 16 那樣使用該命令,那麼將得到一個錯誤。在實際情況中,文件列表可能來自一些源,比如定製腳本或命令,而不是 ls,或者您希望 通過其他管道線階段傳遞它,以進一步進行過濾。所以您應該使用 grep "1" * 取代以上構造。

對於 ls 命令,您可以使用 --quoting-style 選項強制給導致問題的文件名加上引號或進行轉義。另外一種更好的解決辦法是使用 xargs 的 -0 選項,從而使用 null 字符串 (\0) 分隔輸入參數。儘管 ls 沒有提供使用 null 字符串分隔的文件名作爲輸出的選項,但許多命令都提供這樣的選項。

在清單 17 中,我們首先將 text1 複製到 “text 1”,然後顯示一些在 xargs 命令中使用包含空格的文件名列表的方法。這些示例僅爲了演示概念,因爲 xargs 可能更加複雜。尤其是在最後一個例子中, 如果一些文件名已經包含新行字符串,那麼將新行字符串轉換成 null 字符串將導致錯誤。在本文的下一個部分中,我們將查看另外一個更加健壯的解決方案,即使用 find 命令生成合適的以 null 字符串分隔的輸出。

清單 17. 文件名中包含空格的 xargs


				
[ian@echidna lpi103-4]$ cp text1 "text 1"
[ian@echidna lpi103-4]$ ls *1 |xargs grep "1" # error
text1:1 apple
grep: text: No such file or directory
grep: 1: No such file or directory
[ian@echidna lpi103-4]$ ls --quoting-style escape *1
text1 text\ 1
[ian@echidna lpi103-4]$ ls --quoting-style shell *1
text1 'text 1'
[ian@echidna lpi103-4]$ ls --quoting-style shell *1 |xargs grep "1"
text1:1 apple
text 1:1 apple
[ian@echidna lpi103-4]$ # Illustrate -0 option of xargs
[ian@echidna lpi103-4]$ ls *1 | tr '\n' '\0' |xargs -0 grep "1"
text1:1 apple
text 1:1 apple

 

xargs 命令不會構建任意長度的命令。在 Linux 內核 2.26.3 之前,命令的長度是受限制的。針對某個包含大量名稱很長的文件的目錄的命令,比如 rm somepath/*,可能會失敗, 返回的消息表明參數列表太長。在更舊的 Linux 系統或 UNIX 系統上仍然存在該限制,因此瞭解如何使用 xargs 以處理這種問題非常有用。

您可以使用 --show-limits 選項顯示 xargs 的默認限制,然後使用 -s 選項將輸出命令的長度限制在允許的最大字符串數量之內。查看手冊頁瞭解其他未能再次討論的選項。

使用帶有 -exec 選項或 xargs 的 find 命令

在文章 “學習 Linux,101:文件和目錄管理” 中,您學習例如如何使用 find 命令根據名稱、修改時間、大小或其他特徵查找文件。找到匹配的文件集之後,您通常希望對它們執行某些操作:刪除、移動和重命名它們等。現在我們看一下 find 命令的 -exec 選項,其功能類似於使用 find 並通過管道將輸出指向 xargs

清單 18. 使用 find 和 -exec


				
[ian@echidna lpi103-4]$ find text[12] -exec cat text3 {} \;
This is a sentence. This is a sentence. This is a sentence.
1 apple
2 pear
3 banana
This is a sentence. This is a sentence. This is a sentence.
9 plum
3 banana
10 apple

 

與前面學習的 xargs 命令相比,它有幾個不同之處。

  1. 必須使用 {} 標記文件名在命令中的位置。它不是自動添加在末尾的。
  2. 您必須使用轉義後的分號終止該命令,比如 \;、';' 或 ";" 都行。
  3. 該命令對每個輸入文件執行一次。

嘗試運行 find text[12] |xargs cat text3 親自看看區別在哪裏。

現在,我將話題轉回到文件名中的空格。在清單 19 中我們嘗試使用帶有 -exec 的 find, 而不是帶有 xargs 的 ls

清單 19. 對包含空格的文件名使用 find 和 -exec


				
[ian@echidna lpi103-4]$ find . -name "*1" -exec grep "1" {} \;
1 apple
1 apple

 

到目前爲止,一切進展順利。但是不是缺少了什麼?哪個文件包含 grep 找到行?缺少了文件名,因爲 find 爲每個文件調用 grep 一次,而grep 非常智能,能夠知道您是不是僅提供文件名,您不需要它告訴您是哪個文件。

我們也可以改爲使用 xargs,但我們已經看到了文件名中包含空格時出現的問題。我們還提到 find 可以生成一個以 null 分隔符分隔的文件名列表,這是 -print0 選項所起的作用。新的 find 可能使用加號(+)取代分號(;)作爲分隔符,這允許 find 在一次調用命令時傳遞儘可能多的名稱,類似於 xargs。 在這種情況中,僅能使用 {} 一次,並且它必須是該命令的最後一個參數。清單 20 顯示了這兩種方法。

清單 20. 對包含空格的文件名使用 find 和 xargs


				
[ian@echidna lpi103-4]$ find . -name "*1" -print0 |xargs -0 grep "1"
./text 1:1 apple
./text1:1 apple
[ian@echidna lpi103-4]$ find . -name "*1" -exec grep "1" {} +
./text 1:1 apple
./text1:1 apple

 

一般而言,兩種方法都是有效的,選擇哪種方法由您決定。記住,使用管道導出包含未受保護的空格的內容將導致問題,因此如果您要使用管道將輸出 導出到 xargs,請使用將 -print0 選項和 find 結合使用,並使用 -0 選項告訴 xargs 接收使用 null 分隔符分隔的輸入。其他命令,包括 tar,也支持使用 -0 選項並用 null 分隔符分隔的輸入,因此應該對支持該選項的命令使用它,除非您能確保您的輸入列表不會造成問題。

最後,我們介紹對文件列表進行操作。在執行刪除或重命名文件等重要操作之前,最好徹底地測試列表和仔細測試命令。進行良好的備份也是非常有價 值的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章