xargs--冬天裡的一絲暖意

一寸HUI發表於2020-12-04

本文為部落格園作者所寫: 一寸HUI,個人部落格地址:https://www.cnblogs.com/zsql/

你有批量kill作業嗎?有因為刪除資料夾的內容太多而報錯嗎?-bash: /bin/rm: Argument list too long,查詢出來的檔案怎麼直接刪除呢?批量執行?併發執行?如果有這樣的需求的話,就可以開始學習xargs命令了,本文主要的思路先提供一些簡單的使用場景,然後再對xargs命令原理和使用進行闡述。

一、簡單使用場景

1.1、使用場景

  • 傳遞引數,從而組合多個命令
  • 分隔資料,避免引數過長
  • 不支援管道的命令

1.2、具體例項

1.1、批量刪除hive使用者的所有程式

ps aux | grep ^hive | grep -v grep | awk '{print $2}' | xargs kill -9

1.2、分批次刪除logs目錄下2020年的所有檔案,每批為10個

cd  /logs && ls  | grep *2020* | xargs -n 10 rm -fr
ls /logs | sed "s:^:`pwd`/:" | grep *2020* | xargs -n 10 rm -fr

1.3、結合find命令進行處理文件,find命令可以檢視:linux之find命令詳解

find . -name *.log -print0 | xargs rm -f #查詢當前目錄下的所有log檔案,並刪除
find ./ -type f -name "*.txt" | xargs -i cp {}  /tmp/ #查詢txt檔案,並複製到/tmp目錄下
find ./ -type f  -name "*.log" | xargs -i -t  tar -zcvf {}.tar.gz {} #壓縮日誌檔案

1.4、批量下載

cat urls.txt | xargs wget

1.5、如果打通了ssh免密,假如有ip:192.168.88.130,192.168.88.131

echo "192.168.88.130,192.168.88.131" | xargs -n 1 -P 2 -I __ip__ scp root@__ip__:/var/log/cron ./__ip__.cron #把這兩個機器的cron日誌複製到本地,取名為${ip}.cron

二、原理和使用

2.1、使用語法

[root@lgh~]# xargs --help
Usage: xargs [-0prtx] [--interactive] [--null] [-d|--delimiter=delim]
       [-E eof-str] [-e[eof-str]]  [--eof[=eof-str]]
       [-L max-lines] [-l[max-lines]] [--max-lines[=max-lines]]
       [-I replace-str] [-i[replace-str]] [--replace[=replace-str]]
       [-n max-args] [--max-args=max-args]
       [-s max-chars] [--max-chars=max-chars]
       [-P max-procs]  [--max-procs=max-procs] [--show-limits]
       [--verbose] [--exit] [--no-run-if-empty] [--arg-file=file]
       [--version] [--help] [command [initial-arguments]]

2.2、原理說明

  在1.1中我們說了xargs使用的三個場景,一個是引數太長導致命令不能執行,就像rm一樣,如果rm -rf /log/*,正如log目錄下有很多很多個檔案,這樣就會出現/bin/rm: Argument list too long這樣的錯誤,那為啥使用xargs就能刪除了呢,因為xargs預設會把引數擷取分批進行執行,這裡使用了xargs的分割資料和傳遞引數原理,還有就是有些命令並不支援管道符,像ls命令,但是又想使用一行命令搞定,這樣就可以使用xargs配合使用了。比如使用命令:echo text.txt  | xargs cat,這樣可以把text.txt傳遞給cat命令作為引數,這樣就可以檢視該檔案了,也就是說xargs完成了兩個行為:處理管道傳輸過來的stdin;將處理後的傳遞到正確的位置上,還可以將stdin或者檔案stdin分割成批,每個批中有很多分割片段,然後將這些片段按批交給xargs後面的命令進行處理,可能看到這裡會有點懵逼,那簡單點就是:xargs處理的優先順序或順序了:先分割,再分批,然後傳遞到引數位。

  舉個例子:假如你有一個蛋糕,你要分給10個孩子去吃,首先第一步要進行切蛋糕(分割)那怎麼切呢,切10塊還是20塊或者其他呢?所以這裡就會存在切蛋糕方式的選擇(分割方式),切好蛋糕後,小孩子都在不同的地方,你是選擇每次送一塊蛋糕呢,還是一次送兩塊呢(這就是分批,每批次傳送多少個),送也要找到小孩的位置才能送到他手上吧,(這就是把分好批的檔案或資料傳遞到命令指定的引數的位置),我想大家應該比較容易理解這個了,如果你還是沒能理解,多看幾遍:xargs處理的優先順序或順序了:先分割,再分批,然後傳遞到引數位,然後繼續看下面的引數例子就懂了

2.3、引數詳解

2.3.1、分割引數

分割有三種方法:獨立的xargs、xargs -d和xargs -0,後兩者可以配合起來使用,既然都可以用來分割,那我們看看分割的方式:

1、xargs分割:將接收到的stdout處理後傳遞到xargs後面的命令引數位,不寫命令時預設的命令是echo,會對空格、tab、換行符,反斜線等進行分割

[root@lgh test]# echo -e "a b c \n dd \t y"
a b c
 dd      y
[root@lgh test]# echo -e "a b c \n dd \t y" | xargs #預設會使用echo命令輸出,這裡的換行符和空格以及製表符都被作為分隔符用來分割了
a b c dd y
[root@lgh test]# echo -e "a b c \n dd \t y" | xargs echo
a b c dd y

將所有空格、製表符和分行符都替換為空格並壓縮到一行上顯示,這一整行將作為一個整體,這個整體的所有空格屬性繼承xargs處理前的符號屬性(大概理解為,把這些分割好後用自己的所要的“段”替代,幹掉別人的心腹(分隔符),換成自己的心腹)

2、xargs -d分割:可以指定分段符,可以是單個符號、字母或數字。如指定字母o為分隔符:xargs -d"o"

[root@lgh test]# echo -e "a bd\n cod \n dod \t y"
a bd
 cod
 dod     y
[root@lgh test]# echo -e "a bd\n cod \n dod \t y" |xargs -d 'o' #使用o進行分割,但是這樣可能看的不是很清楚
a bd
 c d
 d d     y

這樣看可能就清楚了:

[root@lgh test]# echo -e "a bd\n cod \n dod \t y" |xargs -p -n 1  -d "o"  #-p引數互動引數,-n 1執行每批的個數,這些後面會說到
/bin/echo a bd
 c ?... #列印第一批
/bin/echo d
 d ?... #列印第二批
/bin/echo d      y
 ?... #列印第三批
/bin/echo ?...

從上就可以看出來是指定的字元"o"進行分割了,使用xargs -d分割,會忽略空格、製表符、分行符等這些符號,會把這些當做內容保留下來,如上面的換行符都有保留下來,只有指定的"o"才是分割符

3、xargs -0分割:可以處理接收的stdin中的null字元(\0),其實可以理解是xargs -d的一種特列,等價於xargs -d "\0"

[root@mvxl2685 test]# echo -e "1\tbbb \n uu\0dddd"
1 bbb
uudddd
[root@mvxl2685 test]# echo -e "1\tbbb \n uu\0dddd" | xargs -0
1 bbb
uu dddd

[root@mvxl2685 test]# echo -e "1\tbbb \n uu\0dddd" | xargs -0 -p -n 1
/bin/echo 1 bbb
uu ?... #第一批
/bin/echo dddd
?... #第二批
/bin/echo ?...

上面三種分割方式,使用最多的也就是預設的xargs了

分割行為

特殊符號處理方式

分段方法

配合分批選項

分批方法

xargs

空格、製表符、分行符替換為空格,引號和反斜線刪除。處理完後只有空格。如果空格、製表符和分行符使用引號包圍則可以保留

結果繼承處理前的符號性質(文字符號還是標記意義符號)。

-n

以分段結果中的每個空格分段,進而分批。不管是文字還是標記意義的空格,只要是空格

-L、-i

以標記意義上的空格分段,進而分批

不指定

結果作為整體輸出

xargs -d

xargs -d   不處理文字意義上的符號,所有標記意義上的符號替換為換行符\n,將-d指定的分割符替換為標記意義上的空格。

結果中除了最後的空行和-d指定的分割符位的分段空格,其餘全是文字意義上的符號

按照-d指定的符號進行分段,每個段中可能包含文字意義上的空格、製表符、甚至是分行符。

-n、-L、-i

以標記意義上的符號(即最後的空行和-d指定分隔符位的空格)分段,進而分批。分段結果中保留所有段中的符號,包括製表符和分行符。

不指定

結果作為整體輸出

xargs -0

不處理文字意義上的符號,將非\0的標記意義上的符號替換為\n,將\0替換為空格。

結果中除了最後空行和\0位的空格,其餘都是文字意義上的符號

以替換\0位的空格分段,每個段中可能包含文字意義上的空格、製表符、甚至是分行符。

如果沒檢測到\0,則只有一個不可分割的段。

-n、-L、-i

檢測到\0時,以標記意義上的符號(即最後的空行和\0位的空格)分段,進而分批。分段結果中保留所有段中的符號,包括製表符和分行符。

未檢測到\0時,整個結果作為不可分割整體,使用分批選項是無意義的

不指定

結果作為整體輸出

2.3.2、分批引數

上面一節中我們知道怎麼切蛋糕了(分割),接下來我們就怎麼把這些分割好的內容組裝成批次。可以使用-n和-L兩個引數,那具體他們有什麼區別呢?請繼續往下看:
1、使用-n分批

假如有資料夾test:

[root@lgh test]# ll
total 0
-rw-r--r-- 1 root root 0 Dec  4 14:33 a
-rw-r--r-- 1 root root 0 Dec  4 14:33 b
-rw-r--r-- 1 root root 0 Dec  4 14:33 c
-rw-r--r-- 1 root root 0 Dec  4 14:33 dd
-rw-r--r-- 1 root root 0 Dec  4 14:33 gg
-rw-r--r-- 1 root root 0 Dec  4 14:33 u t.txt #這是個特殊檔案,有空格的檔名
-rw-r--r-- 1 root root 0 Dec  4 14:33 uu

接下我們使用-n分批會有怎樣的效果呢?

[root@lgh test]# ls | xargs -n 2 #每兩個作為一個批次
a b
c dd
gg u  #這裡把u t.txt檔案拆到兩個批次中了,這並不是我們想要的
t.txt uu

xargs -n分兩種情況:和獨立的xargs一起使用,這時按照每個空格分段劃批;和xargs -d或xargs -0一起使用,這時按段分批,即不以空格、製表符和分行符分段劃批

2、使用-L分批

[root@lgh test]# ls | xargs -L 2 #每兩個作為一個批次
a b
c dd
gg u t.txt #這裡就沒有拆分u t.txt檔案
uu

-n選項類似,唯一的區別是-L永遠是按段劃批,而-n在和獨立的xargs一起使用時是按空格分段劃批的

2.3.3、位置傳遞引數

xargs -i和xargs -I選項在邏輯上用於接收傳遞的分批結果,如果不使用這兩個選項,則預設是將分割後處理後的結果整體傳遞到命令的最尾部,有時候可能需要到多個位置,或者不是最後一個位置,例如cp命令一樣,所以這兩個引數很重要。

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

假如有目錄test:

[root@lgh test]# ll
total 0
-rw-r--r-- 1 root root 0 Dec  4 14:51 10.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 1.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 2.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 3.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 4.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 5.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 6.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 7.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 8.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 9.log

現在需要把它們全部備份起來,程式設計*.log.bak檔案

[root@lgh test]# ls | xargs  -i mv  {} {}.bak #這裡使用-i接收,然後使用{}指定位置
[root@lgh test]# ll
total 0
-rw-r--r-- 1 root root 0 Dec  4 14:51 10.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 1.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 2.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 3.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 4.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 5.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 6.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 7.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 8.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 9.log.bak

-I則可以指定其他的符號、字母、數字作為替換符號,例如xargs -I

[root@lgh test]# ls | xargs  -I bb mv  bb bb.I #這裡使用-I引數指定bb為接收替代符
[root@lgh test]# ll
total 0
-rw-r--r-- 1 root root 0 Dec  4 14:51 10.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 1.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 2.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 3.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 4.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 5.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 6.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 7.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 8.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 9.log.bak.I

注意:-i、-L、-n這三個引數在一起,都會被最後一個覆蓋,也就是一起使用的時候,只有最後一個生效

2.3.4、其他引數

上面已經把最重要的一些引數介紹完了,我們看看其他引數

1、-p和-t:互動引數,可以用於前期不確定執行過程,用來測試自己寫的程式碼是否符合最終的執行命令

-p是詢問是否列印,y鍵表示列印,回車表示不列印

[root@lgh test]# ls | xargs -n 3 -p
/bin/echo 10.log.bak.I 1.log.bak.I 2.log.bak.I ?...y #詢問列印,按下y
/bin/echo 3.log.bak.I 4.log.bak.I 5.log.bak.I ?...10.log.bak.I 1.log.bak.I 2.log.bak.I #列印出上一次詢問的值,然後繼續詢問下一個批次是否列印,按下回車,不列印輸出

/bin/echo 6.log.bak.I 7.log.bak.I 8.log.bak.I ?...
/bin/echo 9.log.bak.I ?...

-t是先把執行的語句列印出來,然後把要輸出的內容列印出來

[root@lgh test]# ls | xargs -n 3 -t
/bin/echo 10.log.bak.I 1.log.bak.I 2.log.bak.I #列印執行的語句
10.log.bak.I 1.log.bak.I 2.log.bak.I #列印執行語句的結果
/bin/echo 3.log.bak.I 4.log.bak.I 5.log.bak.I
3.log.bak.I 4.log.bak.I 5.log.bak.I
/bin/echo 6.log.bak.I 7.log.bak.I 8.log.bak.I
6.log.bak.I 7.log.bak.I 8.log.bak.I
/bin/echo 9.log.bak.I
9.log.bak.I

2、-P:批量執行引數(併發,一起執行)哈哈,反正就是同時執行

在執行某一個命令的時候,後面很多引數的時候,例如:rm log/* 會對log下的每一個目錄進行依次刪除,就算使用xargs的分批執行,還是一個一個執行的,並不會提高效率,接下來該-P引數出場了

"-P N"選項可以指定並行處理的程式數量為N。不指定"-P"時,預設為1個處理程式,也就是序列執行。指定為0時,將盡可能多地開啟程式數量

[root@lgh ~]# time echo {1..3} | xargs -n 1 sleep #不使用-P,並每個批次為1,總共的執行時間為6秒

real    0m6.005s
user    0m0.001s
sys     0m0.001s
[root@lgh ~]# time echo {1..3} | xargs -n 3 sleep #不使用-P,每個批次為3,總共執行時間為6秒,分批不能提高速度

real    0m6.003s
user    0m0.000s
sys     0m0.002s
[root@lgh ~]# time echo {1..3} | xargs -n 3 -P 3 sleep #使用-P,每個批次為3,說明只有一個批次,所以一個執行緒需要一次執行所有的命令,所以還是6秒,沒有達到併發的效果

real    0m6.003s
user    0m0.001s
sys     0m0.000s
[root@lgh ~]# time echo {1..3} | xargs -n 1 -P 3 sleep #使用-P,每個批次為1,所以可以同時執行命令,執行時間為3秒

real    0m3.003s
user    0m0.001s
sys     0m0.001s

很多時候,執行大量命令的時候可以使用-P提高效率

2.4、總結

xargs命令很強大,你值得擁有!!!如果使用的得當不僅可以提高逼格,還可以提高效率,能夠做一些原本命令不能做到的事情,好好學號xargs何樂而不為呢?

xargs的總結:分割(xargs,xargs -d,xargs -0)、分批(-n,-L)、傳遞到指定位置(-i,-I)

 

 

參考:

https://www.cnblogs.com/f-ck-need-u/p/5925923.html#auto_id_9

 https://blog.chenyuanwai.com/xargs/

https://blog.csdn.net/lilongsy/article/details/77535687

相關文章