1. 程式人生 > 其它 >Linux那點事-xargs命令詳解

Linux那點事-xargs命令詳解

1. 為什麼需要xargs命令

1.1 管道|的缺陷

管道實現的是將前面的輸出stdout作為後面的輸入stdin,但是有些命令不接受管道的傳遞方式。例如:ls,這是為什麼呢?

因為有些命令希望管道傳遞過來的是引數,但是直接使用管道有時無法傳遞到命令的引數位這時候就需要xargsxargs實現的是將管道傳遞過來的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命令:替換或者刪除字元】

  \0的作用

4. 實際應用:
-xargs -0的包含空格的檔案的操作。

【起因】使用find+rm命令無法刪除帶有空格的檔案。

 

 

 

xargs命令輸出

【原因】:xargs預設是以空白字元(空格、換行符、製表符)來分割記錄的實際上rm刪除的資料便是./onefile.txt

【解決方案】:此時我們不能使用預設的分割符,而應該使用自定義的分割符!我們知道find命令,檔案後面均是換行符

  find命令

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

 

 

xargs -0分段

為什麼要使用\0作為分割符,而不是其他字元呢?因為在程式語言中,一般使用\0作為結束標誌。而檔案的路徑名不可能包含\0

本質上是藉助xargs的對\0的分割操作。find命令將換行符替換成\0。

3. xargs的分批行為

對於xargs,不寫命令時預設的執行是echo

  預設xargs命令將換行處理掉了

將換行處理掉不是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】本質上分為兩種情況:

  1. 和獨立的xargs一起使用,這時按照每個空格分段劃批;
  2. xargs -dxargs -0一起使用,這時按段分批;
  3. xargs -L-n選項類似,唯一的區別就是-L永遠是按段劃批,而-n和獨立的xargs一起使用時是按空格分段劃批的。
 

 

 

-n和-L的區別   -i也不會分割文字意義的空格

4 xargs -i 接收傳遞的分批結果

xargs -i選項在邏輯上用於接收傳遞的分批結果。

如果不使用-i,則預設是將分割處理後的結果整體傳遞到【命令的最尾部】。但是有時候需要傳遞到多個位置,不使用-i就不知道傳遞到哪個位置了。

例如:重新命名備份的時候在每個傳遞過來的檔名加上字尾.bak,這需要兩個引數位。

語法:
使用xargs -i時以大括號{}作為替換符號,傳遞的時候看到{}就將結果替換,可以將{}放在任意需要傳遞的引數位置上。如果多個地方使用{}就實現了多個傳遞。

xargs -Ixargs -i是一樣的,只是-i預設使用大括號作為替換符號,-I可以指定其他符號、字母、數字作為替換符號,但是必須用引號包起來。man推薦使用-I代替-i,但是一般使用-i方便,除非在命令中不能使用{},例如touch {1...10}.log時,大括號就不能用來做替換符號。

分析:重新命名備份的時候在每個傳遞過來的檔名加上字尾.bak

案例分析1:

./指的是當前目錄】

  1. 重新命名邏輯是:mv ./logdir/a.log ./logdir/a.log.bak
  2. 我們想將一個目錄下的檔案都要執行某些邏輯。

命令:ls logdir/ | xargs -i mv ./logdir/{} ./logdir/{}.bak

為什麼將“-i”選項劃分在分批選項裡面,因為它預設一個段就是一個批,每次傳遞一個批就是傳遞一個段到指定大括號{}位上。不理解,可以看下1.2.4 分批選項的生效規則

  xargs的-i欄位的使用

案例分析2:

例如:想將數字1-10沒三個數顯示在startend之間。

start 1 2 3 end 
start 4 5 6 end 
start 7 8 9 end 
start 10 end 

由於指定了引數傳遞位置,所以必須使用-i,那就無法一次傳遞3個數,要解決這個問題,就要想辦法讓每三個樹分一次段,然後後使用-i傳遞,那麼可以將每三個數分一次行寫入一個檔案。
例如:

  xargs -i分段操作

 

當然,也可以多次使用xargs。在很多使用無法解決分段的問題都可以通過多次使用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命令的引數處。

    find+grep