Linux那點事-xargs命令詳解
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替換為分段空格後,切分獨立整體。
使用-d分段xargs -d "o"進行自定義分割後,然後分批,我們可以看到,實際上分成了2批。
使用-n檢視-d的分批情況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命令輸出
【原因】:xargs預設是以空白字元(空格、換行符、製表符)來分割記錄的,實際上rm
刪除的資料便是./one
和file.txt
。而
【解決方案】:此時我們不能使用預設的分割符,而應該使用自定義的分割符!我們知道find
命令,檔案後面均是換行符。
【解決方案】為了解決這個問題,可以在每個檔案將換行符替換為NULL(\0
)。這樣我們以\0
分割,就可以得到完整檔案。
xargs -0分段
為什麼要使用\0
作為分割符,而不是其他字元呢?因為在程式語言中,一般使用\0
作為結束標誌。而檔案的路徑名不可能包含\0
。
本質上是藉助xargs的對\0的分割操作。find命令將換行符替換成\0。
3. xargs的分批行為
預設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
一起使用時是按空格分段劃批的。
-n和-L的區別 -i也不會分割文字意義的空格
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在-n之後,-n將被忽略
實際上,-i
就是隱含了-L 1
,-i
是分批並傳遞這兩個作用跟嚴格些。
6. xargs觀察命令的執行過程
使用-p
選項是互動詢問式的,只有每次詢問的時候輸入y(或者yes)
才會執行,直接按entry
鍵是不會執行的。
使用-t
選項是在每次執行xargs
後面的命令都會先在stderr
上列印一遍命令的執行過程然後才正式執行。
使用-p
和-t
選項就可以根據xargs
後面的命令的執行順序進行推測,xargs
是如何分段,分批以及傳遞的。
-p或者-t引數如何使用
【後續】 分批選項的的典型應用
1. 同一目錄下檔案過多
分批選項有時特別有用,例如指令碼規定每次只能傳遞三個引數,有時候grep
或者rm -rf
檔案數量特別多的時候會提示引數列表過長
而導致失敗,這時候就可以分批來按批查詢或刪除。
命令:ls | xargs -n 10000 rm -rf
2. xargs+find的使用
xargs原本就是為find而開發的。
find命令將匹配到的檔案傳遞給xargs
命令,而xargs
命令每次只獲取一部分而不是全部。不像-exec
選項那樣,這樣就可以先處理最先獲取的一部分檔案,然後是下一批。
實際應用:
ls+grep
跨目錄查詢時,我們將檔案通過管道輸入到grep
引數處,此時並沒有包含目錄地址,邊會出現下面的錯誤。
ls + grep 跨目錄查詢
解決方案:
將find
找到的檔案地址傳遞給grep
命令的引數處。