1. 為什么需要xargs命令
1.1 管道|的缺陷
管道實現的是將前面的輸出stdout
作為后面的輸入stdin
,但是有些命令不接受管道的傳遞方式。例如:ls
,這是為什么呢?
因為有些命令希望管道傳遞過來的是參數,但是直接使用管道有時無法傳遞到命令的參數位。這時候就需要xargs
,xargs
實現的是將管道傳遞過來的stdin
進行處理然后傳遞到命令的參數位置上。
用戶查找文件:

用戶希望處理查找后的文件:

也就是xargs完成了兩個行為:
1. 處理管道傳輸過來的
stdin
;
2. 將處理后的數據傳遞到正確的位置;
1.2 xargs對數據的處理
處理大量數據的時候,可能會發生參數列表過長的情況。而xargs將完成參數的定位我們清楚,但是
xrags
如何處理管道傳輸的stdin
呢?
其實就是完成兩個操作:
1. 對數據的分割;
2. 對數據的分批;
xargs處理的優先級或順序:先分割,在分批,然后傳遞到參數位。
可以設想一個場景,我想對一堆數據進行處理,實際上是對一堆中的每個數據進行分別的處理。那么如何將一堆數據按照自定義規則分割為獨立的數據?若是一次性傳遞的數據過多,又該如何處理?
1.2.1 xrags的並發處理
但需要注意的是,盡管實現了分批處理,但是默認情況下並沒有提高任何效率,因為分批傳遞之后還是一次執行一個。而且有時候分批之后是將其作為一個參數的整體,並不會將分批中的信息分段執行。
但事實上,xargs提供
了-P
選項,用於指定並發執行的數量(默認是只要一個處理進程,不會提供效率,但是可以指定為N個子進程,或者指定為0表示盡可能多的利用CPU
)。這樣就能將讓分批操作更好的利用多核CPU,從而提升效率。例如上面分成了兩批,指定-P 2
可以並發執行兩批,而並非執行完第一批之后再執行第二批。
剩下的功能就是處理xargs
的細節問題了,比如如何分割(xargs、xargs -d、xargs -0)
,分割后如何划批(xargs -n、xargs -L)
,參數如何傳遞(xargs -i)
。另外xargs
還提供了詢問交互處理(-p
選項)和預先打印一遍命令執行情況(-t
選項),傳遞終止符(-E
選項)等。
1.2.2 執行命令准備
命令准備:
[root@xuexi tmp]$ cd /tmp [root@xuexi tmp]$ rm -fr * [root@xuexi tmp]$ mkdir a b c d test logdir shdir [root@xuexi tmp]$ touch "one space.log" [root@xuexi tmp]$ touch logdir/{1..10}.log [root@xuexi tmp]$ touch shdir/{1..5}.sh [root@xuexi tmp]$ echo "the second sh the second line" > shdir/2.sh [root@xuexi tmp]$ cat <<eof>shdir/1.sh > the first sh > the second line > eof
1.2.3 Linux空格含義
那么按照什么規則進行“分割”以及“分批”呢?
Linux文件空格分類(重點):

- 正方形(
\t
)代表:(標記意義)制表符; - 圓形(空格)代表:(文本意義)普通空格;
- 長方形(
\n
)代表:(標記意義)換行符;

- 橢圓形代表:(文本意義)換行符;
- 長方形代表:(標記意義)換行符;
2. xrags的分割行為
xargs
命令會將接收的stdin所有的空白(空格、制表符、換行符)都轉換為空格。我們使用xargs -d "xx"
自定義規則對數據進行切分。默認情況下,xargs使用空格來切分數據。
注意事項
- xargs -d可以指定分割符,可以是單個符號、字母或者數字。如指定
o
為分割符:xargs -d "o"
; - xargs -d是分割階段的選項,所以會優先於分批選項
(-n、-L、-i)
- xargs -d不是先
xargs
在-d
處理的,它是區別於獨立的xargs
的另外一個分割選項。
1. xargs -d原理
-
替換:將接收stdin所有的【標記意義】的符號替換為\n,替換完成后所有的符號(空格、制表符、分行符)變成【文本意義】上的普通符號。
-
分段:根據-d指定的分隔符進行分段,並用空格分開每段,由於發生了【替換】操作,所以符號都是【文本意義】上的。會導致分段中可能包含了空格、制表符、分行符。也就是說處了-d導致的【分段空格】,其余所有符號都是分段中的一部分。
-
輸出:最后根據指定的【分批選項】來輸出。
2. 案例分析
默認情況下,使用空格進行分割,但若是指定自定義分割符(此處使用o),那么將o替換為分段空格后,切分獨立整體。

xargs -d "o"進行自定義分割后,然后分批,我們可以看到,實際上分成了2批。

3. xrags -0命令
xargs -0
的行為和xargs -d
基本一樣,只是-d
是指定分割符,-0
是指定固定的\0
作為分割符。等價於xargs -d "\0"
。
(注意)xargs -0
可以處理接收到的stdin
中的null字符(\0)
。如果不使用-0
選項或-null
選項,檢測到\0
后會給出警告提醒,並只向命令傳遞非\0
段。
【tr
命令:替換或者刪除字符】

4. 實際應用:
-xargs -0的包含空格的文件的操作。
【起因】使用find+rm命令無法刪除帶有空格的文件。

【原因】:xargs默認是以空白字符(空格、換行符、制表符)來分割記錄的,實際上rm
刪除的數據便是./one
和file.txt
。而
【解決方案】:此時我們不能使用默認的分割符,而應該使用自定義的分割符!我們知道find
命令,文件后面均是換行符。

【解決方案】為了解決這個問題,可以在每個文件將換行符替換為NULL(\0
)。這樣我們以\0
分割,就可以得到完整文件。

為什么要使用\0
作為分割符,而不是其他字符呢?因為在編程語言中,一般使用\0
作為結束標志。而文件的路徑名不可能包含\0
。
本質上是借助xargs的對\0的分割操作。find命令將換行符替換成\0。
3. xargs的分批行為
對於xargs,不寫命令時默認的執行是
echo
。

將換行處理掉不是echo
實現的,而是管道傳遞過來的stdin經過xargs處理后得到的。將所有【文本/標記意義】空格、制表符和分行符都替換為【文本意義】上空格並壓縮到一行顯示。
【注意】這一整行將作為一個整體。這個整體可能直接交給命令或者作為stdout
通過管道傳遞給管道右邊的命令,這時結果將作為一個整體傳遞,也有可能被xargs
同時指定的【分批選項】分批處理。(這也是可能出現參數列表過長的原因)
xargs分批命令總結:
-
xargs -n :和獨立的xargs命令配合使用時,按照默認分割符(空格)進行分批,但配合xargs -d命令,則按自定義分割符分批。
-
xargs -L
和-n
選項類似,唯一的區別就是-L
永遠是按段划批,而-n
和獨立的xargs
一起使用時是按空格分段划批的。
2. 對獨立的xargs
指定分批選項
-
【標記/文本】指定
-n
時按空格分段,然后划批,不管是文本意義空格還是標記意義的空格,只要是空格都是-n
的操作對象。 -
【標記】指定
-L
或者-i
時按段划批,文本意義的符號不被處理。
【需要注意的是】:【xargs -n】本質上分為兩種情況:
- 和獨立的
xargs
一起使用,這時按照每個空格分段划批;- 和
xargs -d
或xargs -0
一起使用,這時按段分批;xargs -L
和-n
選項類似,唯一的區別就是-L
永遠是按段划批,而-n
和獨立的xargs
一起使用時是按空格分段划批的。


4 xargs -i 接收傳遞的分批結果
xargs -i
選項在邏輯上用於接收傳遞的分批結果。
如果不使用
-i
,則默認是將分割處理
后的結果整體傳遞到【命令的最尾部】。但是有時候需要傳遞到多個位置,不使用-i
就不知道傳遞到哪個位置了。
例如:重命名備份的時候在每個傳遞過來的文件名加上后綴.bak
,這需要兩個參數位。
語法:
使用xargs -i
時以大括號{}
作為替換符號,傳遞的時候看到{}
就將結果替換,可以將{}
放在任意需要傳遞的參數位置上。如果多個地方使用{}
就實現了多個傳遞。
xargs -I
和xargs -i
是一樣的,只是-i
默認使用大括號作為替換符號,-I
可以指定其他符號、字母、數字作為替換符號,但是必須用引號
包起來。man推薦使用-I
代替-i
,但是一般使用-i
方便,除非在命令中不能使用{}
,例如touch {1...10}.log
時,大括號就不能用來做替換符號。
分析:重命名備份的時候在每個傳遞過來的文件名加上后綴
.bak
。
案例分析1:
【./
指的是當前目錄】
- 重命名邏輯是:
mv ./logdir/a.log ./logdir/a.log.bak
- 我們想將一個目錄下的文件都要執行某些邏輯。
命令:ls logdir/ | xargs -i mv ./logdir/{} ./logdir/{}.bak
為什么將“-i”
選項划分在分批選項里面,因為它默認一個段就是一個批,每次傳遞一個批就是傳遞一個段到指定大括號{}位上。不理解,可以看下1.2.4 分批選項的生效規則

案例分析2:
例如:想將數字1-10
沒三個數顯示在start
和end
之間。
start 1 2 3 end start 4 5 6 end start 7 8 9 end start 10 end
由於指定了參數傳遞位置,所以必須使用-i
,那就無法一次傳遞3個數,要解決這個問題,就要想辦法讓每三個樹分一次段,然后后使用-i
傳遞,那么可以將每三個數分一次行寫入一個文件。
例如:

當然,也可以多次使用xargs
。在很多使用無法解決分段的問題都可以通過多次使用xargs
來解決。

5. 分批選項的生效規則
-i、-L、-n
選項都是分批選項,他們的生效規則是:誰指定在后面,誰就生效!!!

實際上,-i
就是隱含了-L 1
,-i
是分批並傳遞這兩個作用跟嚴格些。
6. xargs觀察命令的執行過程
使用-p
選項是交互詢問式的,只有每次詢問的時候輸入y(或者yes)
才會執行,直接按entry
鍵是不會執行的。
使用-t
選項是在每次執行xargs
后面的命令都會先在stderr
上打印一遍命令的執行過程然后才正式執行。
使用-p
和-t
選項就可以根據xargs
后面的命令的執行順序進行推測,xargs
是如何分段,分批以及傳遞的。

【后續】 分批選項的的典型應用
1. 同一目錄下文件過多
分批選項有時特別有用,例如腳本規定每次只能傳遞三個參數,有時候grep
或者rm -rf
文件數量特別多的時候會提示參數列表過長
而導致失敗,這時候就可以分批來按批查詢或刪除。
命令:ls | xargs -n 10000 rm -rf
2. xargs+find的使用
xargs原本就是為find而開發的。
find命令將匹配到的文件傳遞給xargs
命令,而xargs
命令每次只獲取一部分而不是全部。不像-exec
選項那樣,這樣就可以先處理最先獲取的一部分文件,然后是下一批。
實際應用:
ls+grep
跨目錄查詢時,我們將文件通過管道輸入到grep
參數處,此時並沒有包含目錄地址,邊會出現下面的錯誤。

解決方案:
將find
找到的文件地址傳遞給grep
命令的參數處。
