1. 程式人生 > >如何在Linux上使用xargs命令

如何在Linux上使用xargs命令

大家好,我是良許。 在使用 Linux 時,你是否遇到過需要將一些命令串在一起,但是其中一個命令不接受管道輸入的情況呢?在這種情況下,我們就可以使用 `xargs` 命令。`xargs` 可以將一個命令的輸出作為引數傳送給另一個命令。 在 Linux 中,所有標準的應用程式都有與之關聯的三個資料流。分別是標準輸入流(stdin),標準輸出流(stdout)和標準錯誤流(stderr)。這些流通過文字來執行,我們使用文字將輸入(stdin)傳送到命令,然後響應(stdout)將會以文字形式顯示在終端視窗上。錯誤訊息也以文字的形式顯示在終端視窗上(stderr)。 Linux 和類 Unix 作業系統的一大功能是可以將一個命令的標準輸出流傳遞到另一個命令的標準輸入流。第一個命令不會管它的輸出是否寫到了終端視窗,第二個命令也不會管它的輸入是否來自鍵盤。 雖然所有 Linux 命令都有三個標準流,但是並不是所有命令都接受另一個命令的標準輸出作為它的標準輸入流的輸入。因此我們無法通過管道將輸入傳給這些命令。 `xargs`是一個使用標準資料流構建執行管道的命令。通過使用`xargs`命令我們可以使 `echo`,`rm` 和 `mkdir` 等命令接受標準輸入作為它們的引數。 #### xargs命令 `xargs`接受管道輸入,也可以接受來自檔案的輸入。`xargs` 使用該輸入作為我們指定的命令的引數。如果我們沒有給`xargs`指定特定的命令,則預設使用 `echo`。`xargs` 始終生成單行輸出,即使輸入的資料是多行的。 假如我們使用 `ls` 的 `-1`(每行列出一個檔案)選項,則會得到一列檔名稱: ``` $ ls -1 ./*.sh ``` 這一命令列出了當前目錄中的 Shell 指令碼檔案。 ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215734630-1554318736.jpg) 如果我們將輸出結果通過管道傳遞給 `xargs` ,會得到什麼樣的效果? ``` $ ls -1 ./*.sh | xargs ``` ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215734795-1464464433.jpg) 可以看出來,輸出以一長串文字的形式寫到了終端上。由此可見,`xargs` 可以將輸出作為引數傳遞給其他命令。 #### 配合wc命令使用xargs 我們可以使用 `xargs` 命令輕鬆地讓 `wc` 命令計算多個檔案中的單詞數,字元數和行數 ``` $ ls *.c | xargs wc ``` 執行結果如下: ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215734982-473966218.jpg) 命令執行結果顯示了每個檔案的統計資訊以及總數。 這條命令執行了以下操作: - `ls`列出了所有的 .page 檔案,並將該列表傳給了`xargs`。 - `xargs` 將所有檔名傳遞給 `wc`。 - `wc` 將這些檔名作為命令列引數進行處理。 #### 使用帶有確認訊息的xargs 我們可以使用 `-p`(互動)選項來讓 `xargs` 提示我們是否要進行下一步的操作。 如果我們通過 `xargs` 將一串檔名的字串傳遞給 `touch` 命令,`touch` 將建立這些檔案。 ``` $ echo 'one two three' | xargs -p touch ``` ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215735230-1498713470.jpg) 終端上顯示將要執行的命令,`xargs` 等待我們輸入 `y` 或 `Y` ,`n` 或 `N` 並按 Enter 來響應。如果只按了 `Enter` ,則視為 `n`。只有當我們當輸入 `y` 或 `Y` 時才執行該命令。 我們按下`y`和 Enter ,然後使用`ls`用來檢查檔案是否已經建立。 ``` $ ls one two three ``` ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215735445-326120557.jpg) #### 將xargs與多個命令一起使用 我們可以用`-I`(初始引數)選項來將 `xargs` 和多個命令一起使用。這一選項定義了`替換字串`。在命令列中的任何出現替換字串的位置,都會插入我們提供給 `xargs` 的值 。 有點抽象,我們以一個例項來進行講解。 我們先用 `tree` 命令檢視當前目錄中的子目錄。該 `-d`(directory)選項使 `tree` 命令忽略檔案,只輸出目錄。 ``` $ tree -d ``` ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215735637-493611508.jpg) 現在只有一個子目錄 images 。 在 directories.txt 這個檔案中,我們有一些想要建立的目錄的名稱。我們先用 `cat` 檢視其中的內容。 ``` $ cat directories.txt ``` ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215735808-1537876613.jpg) 我們把這些內容作為輸入資料傳給 `xargs` ,執行以下的命令: ``` $ cat directories.txt | xargs -I % sh -c 'echo %; mkdir %' ``` 這條命令執行了以下操作: - **cat directories.txt** :將 directrories.txt 檔案的內容(所有要建立的目錄名稱)傳給 `xargs`。 - **xargs -I %** :定義了替換字串 `%`。 - **sh -c**:啟動一個新的子shell。`-c`(commond)讓 shell 讀取命令。 - **'echo %; mkdir %'**:每個`%`都會被替換為 `xargs` 傳過來的目錄名稱 。`echo `命令列印目錄名稱,`mkdir` 命令建立目錄。 命令執行結果: ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215735949-125459085.jpg) 我們可以用 `tree` 驗證已建立是否已建立了目錄。 ``` $ tree -d ``` ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215736157-388466601.jpg) #### 將檔案複製到多個位置 我們可以使用 `xargs` 命令來用一個命令將檔案複製到多個位置。 首先,通過管道將兩個目錄的名稱傳給 `xargs` 。並且讓 `xargs` 一次只將其中一個引數傳遞給正在使用的命令。 想要呼叫 `cp` 兩次,每次各使用兩個目錄中的一個作為命令列引數,我們可以通過將 `xargs` 的 `-n`(max number)選項設定為 1 來實現。 這裡還使用了`-v`(verbose 詳細資訊)選項,讓 `cp` 反饋正在執行的操作。 ``` $ echo ~/dir1/ ~/dir2/ | xargs -n 1 cp -v ./*.c ``` 我們將檔案複製到了兩個目錄,一次複製一個目錄。`cp` 反饋了詳細資訊,讓我們看到進行了哪些操作。 ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215736389-2053630709.jpg) #### 刪除巢狀目錄中的檔案 如果檔名中包含空格或者其他特殊字元(例如換行符),`xargs` 將無法正確解釋這些檔名。我們可以使用 `-0`(空終止符)選項來解決這一問題。此時, `xargs` 將使用 `null` 字元作為檔名最終的分隔符。 這裡我們以 `find` 命令為例。`find` 有自己的選項來處理檔名中的空格和特殊字元,即 `-print0`(全名,空字元)選項。 ``` $ find . -name "*.png" -type f -print0 | xargs -0 rm -v -rf "{}" ``` 這一命令執行了以下操作: - **find . -name “*.png”** :`find` 將從當前目錄中搜索名稱和 *.png 相匹配的物件,`type -f` 指定了只搜尋檔案。 - **-print0**:名稱將以空字元結尾,並且保留空格和特殊字元。 - **xargs -0**:`xargs` 也將考慮檔名以空值結尾,並且空格和特殊字元不會引起問題。 - **rm -v -rf "{}"**:`rm` 將反饋正在進行的操作(`-v`),遞迴進行操作(-r),不傳送錯誤提示而直接刪除檔案(`-f`)。每個檔名替換 "{}"。 命令執行之後,將搜尋了所有子目錄,刪除了其中匹配的檔案。 ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215736563-1459096161.jpg) #### 刪除巢狀目錄 假設我們要刪除一組巢狀的子目錄,先用`tree`進行檢視。 ``` $ tree -d ``` ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215736787-308970767.jpg) ``` $ find . -name "level_one" -type d -print0 | xargs -0 rm -v -rf "{}" ``` 這條命令使用 find 在當前目錄中遞迴搜尋,搜尋的目標是名為 level_one 的目錄,然後將目錄名通過`xargs`傳遞給 `rm` 。 這個命令和前面的命令之間的區別是,搜尋的專案是最頂層目錄的名稱,而且`-type d`說明要查詢的目錄,而不是檔案。 ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215737460-1950237754.jpg) 每個目錄的名稱都在刪除時打印出來。我們可以用`tree`再檢視效果: ``` $ tree -d ``` ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215737677-1981516714.jpg) 所有巢狀的子目錄已刪除了。 #### 刪除一種檔案型別以外的所有檔案 我們可以使用 `find` ,`xargs` 和 `rm` 刪除所有型別的檔案而只保留一種我們想要保留的型別的檔案。這需要提供想要保留的檔案型別。 `-not` 選項讓 `find` 返回所有與搜尋模式不匹配的檔名。我們此時再次使用 `xargs` 的 `-I` (初始引數)選項。這次定義的替換字串為 `{}` 。這和我們之前使用的替換字串 `%` 的效果是相同的。 ``` $ find . -type f -not -name "*.sh" -print0 | xargs -0 -I {} rm -v {} ``` ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215737854-78098036.jpg) 命令執行之後,我們再通過 `ls` 來確認結果。可以看到,目錄中只剩下了與 `*.sh` 相匹配的檔案。 ``` $ ls -l ``` ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215738030-457119456.jpg) #### 使用Xargs建立壓縮檔案 我們可以使用 `find` 命令來搜尋檔案,並通過 `xargs` 將檔名傳給 `tar` 命令來建立壓縮檔案。 我們將在當前目錄中搜索 `* .sh` 檔案。 ``` $ find ./ -name "*.sh" -type f -print0 | xargs -0 tar -cvzf script_files.tar.gz ``` 命令執行結果將列出了所有 .sh 檔案,並建立了壓縮檔案。 ![](https://img2020.cnblogs.com/other/1218435/202008/1218435-20200824215738186-1299033260.jpg) --- 公眾號:良許Linux ### 有收穫?希望老鐵們來個三連擊,給更多的人看到這