在說xargs命令之前,先說兩句linux中的管道(pipe)。
管道負責單向連接前一個程序的標準輸出與后一個程序的標準輸入,其本質是一個共享文件。我們日常最常用到的管道是匿名管道,Shell中的管道符號為“|”。
有空會讀讀與管道有關的內核源碼的,比如pipe()
管道用法舉例:
- 打印/usr/lib目錄下的內容,按大小排序,并分頁顯示行號
ll -Sh /usr/lib | less -N
- 取出歷史登錄信息中的用戶名,排序、去重并計數
last | cut -d ' ' -f 1 | sort | uniq -c
- 殺掉所有正在運行的redis-cli進程
kill -15 `ps aux | grep redis-cli | grep -v grep | awk '{print $2}'`
那么xargs命令又和管道有什么關系呢?如果把上面的第三個例子換一種寫法:
這說明kill命令沒有接收到應有的參數。還有很多命令(比如最常見的mkdir、rm、cp等等等)都不會從標準輸入讀取內容,這時如果在管道符后加上xargs,再加上要執行的命令,那么前一個程序的標準輸出就會作為后一個程序的參數,而不是標準輸入了。
Dash軟件中xargs命令的man page
看官可以嘗試執行以下兩對命令,看看輸出有什么不同,能夠加深理解:
- echo '--help' | cat與echo '--help' | xargs cat
- 隨便找一個Spring項目的代碼目錄,分別執行find . -name '*.JAVA' | grep Impl與find . -name '*.java' | xargs grep Impl
同樣地,上面例子中的kill命令應該改寫成:
ps aux | grep redis-cli | grep -v grep | awk '{print $2}' | xargs kill -15
xargs命令本身也有一些參數,使用方法很靈活,下面再舉幾個例子。
- 自定義分隔符
- xargs默認用IFS(inner field separator,即內部域分隔符,包含空格、制表符、換行符)分割上一個程序的標準輸出。如果要用其他分隔符,可以用-d參數,例如:
~ echo '1,2,3,4,5' | xargs -d ',' echo 1 2 3 4 5
特別地,用-0(注意是數字0)參數可以指定NULL字符''作為分隔符。
- 分批傳參
- 用-n參數可以規定每次傳遞多少個參數給后面的命令,它可以單用,也可以與-d配合使用,例如:
~ echo '1,2,3,4,5,6,7,8,9' | xargs -d ',' -n 4 echo 1 2 3 4 5 6 7 8 9
- 指定終止符
- 用-E參數可以指定xargs在遇到哪個字符串時停止解析(不含該字符串)。-E不能與-d同用,例如:
~ echo 'cd pwd ls ps' | xargs -E 'ls' echo cd pwd
- 交互式執行
- 用-p參數就會在每次執行命令之前詢問,輸入y(yes)才會真正執行。如果只是想觀察命令本身,可以用-t參數。例如:
~ echo '1,2,3,4,5,6,7,8,9' | xargs -d ',' -n 4 -t echo echo 1 2 3 4 1 2 3 4 echo 5 6 7 8 5 6 7 8 echo 9 9
- 指定替換參數位置
- xargs接收的參數默認會分配到下一個命令的結尾,即追加。如果想要指定替換的參數位置,可以用-I或者-i參數。其中,-i固定用雙大括號{}作為占位符,-I則可以自行指定其他符號作為占位符。這個東西很有用,例如:
# 計算表達式的值 ~ echo '77' | xargs -I 'q' expr 2 * q + 8 162 # 批量重命名目錄下的文件 ~ ls | xargs -i mv {} {}.bak # 批量復制目錄下的文件 ~ find . -name "*.jar" | xargs -i cp {} /opt/cloudera/jars
mv、cp等命令對參數列表的長度有限制,如果按普通方法一次操作太多文件,會報“Argument list too long”錯誤。而xargs每次傳參時是以默認128KB的批次進行,所以一切正常。批次大小還可以用-s參數自行指定。
作者:LittleMagic
鏈接:https://www.jianshu.com/p/0aa07a901825
來源:簡書