歡迎光臨
每天分享高質量文章

使用 GNU Parallel 提高 Linux 命令列執行效率 | Linux 中國

將您的計算機變成一個多工的動力室。
— Seth Kenlon


致謝
編譯自 | https://opensource.com/article/18/5/gnu-parallel 
 作者 | Seth Kenlon
 譯者 | Andy Song (pinewall) ? ? ? 共計翻譯:19 篇 貢獻時間:73 天

將您的計算機變成一個多工的動力室。

你是否有過這種感覺,你的主機執行速度沒有預期的那麼快?我也曾經有過這種感覺,直到我發現了 GNU Parallel。

GNU Parallel 是一個 shell 工具,可以並行執行任務。它可以解析多種輸入,讓你可以同時在多份資料上執行指令碼或命令。你終於可以使用全部的 CPU 了!

如果你用過 xargs,上手 Parallel 幾乎沒有難度。如果沒有用過,這篇教程會告訴你如何使用,同時給出一些其它的用例。

安裝 GNU Parallel

GNU Parallel 很可能沒有預裝在你的 Linux 或 BSD 主機上,你可以從軟體源中安裝。以 Fedora 為例:

  1. $ sudo dnf install parallel

對於 NetBSD:

  1. # pkg_add parallel

如果各種方式都不成功,請參考專案主頁[1]

從序列到並行

正如其名稱所示,Parallel 的強大之處是以並行方式執行任務;而我們中不少人平時仍然以序列方式執行任務。

當你對多個物件執行某個命令時,你實際上建立了一個任務佇列。一部分物件可以被命令處理,剩餘的物件需要等待,直到命令處理它們。這種方式是低效的。只要資料夠多,總會形成任務佇列;但與其只使用一個任務佇列,為何不使用多個更小規模的任務佇列呢?

假設你有一個圖片目錄,你希望將目錄中的圖片從 JEEG 格式轉換為 PNG 格式。有多種方法可以完成這個任務。可以手動用 GIMP 開啟每個圖片,輸出成新格式,但這基本是最差的選擇,費時費力。

上述方法有一個漂亮且簡潔的變種,即基於 shell 的方案:

  1. $ convert 001.jpeg 001.png

  2. $ convert 002.jpeg 002.png

  3. $ convert 003.jpeg 003.png

  4. ... ...

對於初學者而言,這是一個不小的轉變,而且看起來是個不小的改進。不再需要影象介面和不斷的滑鼠點選,但仍然是費力的。

進一步改進:

  1. $ for i in *jpeg; do convert $i $i.png ; done

至少,這一步設定好任務執行,讓你節省時間去做更有價值的事情。但問題來了,這仍然是序列操作;一張圖片轉換完成後,佇列中的下一張進行轉換,依此類推直到全部完成。

使用 Parallel:

  1. $ find . -name "*jpeg" | parallel -I% --max-args 1 convert % %.png

這是兩條命令的組合:find 命令,用於收集需要操作的物件;parallel 命令,用於物件排序並確保每個物件按需處理。

◈ find . -name "*jpeg" 查詢當前目錄下以 jpeg 結尾的所有檔案。
◈ parallel 呼叫 GNU Parallel。
◈ -I% 建立了一個佔位符 %,代表 find 傳遞給 Parallel 的內容。如果不使用佔位符,你需要對 find 命令的每一個結果手動編寫一個命令,而這恰恰是你想要避免的。
◈ --max-args 1 給出 Parallel 從佇列獲取新物件的速率限制。考慮到 Parallel 執行的命令只需要一個檔案輸入,這裡將速率限制設定為 1。假如你需要執行更複雜的命令,需要兩個檔案輸入(例如 cat 001.txt 002.txt > new.txt),你需要將速率限制設定為 2。
◈ convert % %.png 是你希望 Parallel 執行的命令。

組合命令的執行效果如下:find 命令收集所有相關的檔案資訊並傳遞給 parallel,後者(使用當前引數)啟動一個任務,(無需等待任務完成)立即獲取引數行中的下一個引數(LCTT 譯註:管道輸出的每一行對應 parallel 的一個引數,所有引數構成引數行);只要你的主機沒有癱瘓,Parallel 會不斷做這樣的操作。舊任務完成後,Parallel 會為分配新任務,直到所有資料都處理完成。不使用 Parallel 完成任務大約需要 10 分鐘,使用後僅需 3 至 5 分鐘。

多個輸入

只要你熟悉 find 和 xargs (整體被稱為 GNU 查詢工具,或 findutils),find 命令是一個完美的 Parallel 資料提供者。它提供了靈活的介面,大多數 Linux 使用者已經很習慣使用,即使對於初學者也很容易學習。

find 命令十分直截了當:你向 find 提供搜尋路徑和待查詢檔案的一部分資訊。可以使用萬用字元完成模糊搜尋;在下麵的例子中,星號匹配任何字元,故 find 定位(檔案名)以字元 searchterm 結尾的全部檔案:

  1. $ find /path/to/directory -name "*searchterm"

預設情況下,find 逐行傳回搜尋結果,每個結果對應 1 行:

  1. $ find ~/graphics -name "*jpg"

  2. /home/seth/graphics/001.jpg

  3. /home/seth/graphics/cat.jpg

  4. /home/seth/graphics/penguin.jpg

  5. /home/seth/graphics/IMG_0135.jpg

當使用管道將 find 的結果傳遞給 parallel 時,每一行中的檔案路徑被視為 parallel 命令的一個引數。另一方面,如果你需要使用命令處理多個引數,你可以改變佇列資料傳遞給 parallel 的方式。

下麵先給出一個不那麼實際的例子,後續會做一些修改使其更加有意義。如果你安裝了 GNU Parallel,你可以跟著這個例子操作。

假設你有 4 個檔案,按照每行一個檔案的方式列出,具體如下:

  1. $ echo ada > ada ; echo lovelace > lovelace

  2. $ echo richard > richard ; echo stallman > stallman

  3. $ ls -1

  4. ada

  5. lovelace

  6. richard

  7. stallman

你需要將兩個檔案合併成第三個檔案,後者同時包含前兩個檔案的內容。這種情況下,Parallel 需要訪問兩個檔案,使用 -I% 變數的方式不符合本例的預期。

Parallel 預設情況下讀取 1 個佇列物件:

  1. $ ls -1 | parallel echo

  2. ada

  3. lovelace

  4. richard

  5. stallman

現在讓 Parallel 每個任務使用 2 個佇列物件:

  1. $ ls -1 | parallel --max-args=2 echo

  2. ada lovelace

  3. richard stallman

現在,我們看到行已經併合並;具體而言,ls -1 的兩個查詢結果會被同時傳送給 Parallel。傳送給 Parallel 的引數涉及了任務所需的 2 個檔案,但目前還只是 1 個有效引數:(對於兩個任務分別為)“ada lovelace” 和 “richard stallman”。你真正需要的是每個任務對應 2 個獨立的引數。

值得慶幸的是,Parallel 本身提供了上述所需的解析功能。如果你將 --max-args 設定為 2,那麼 {1} 和 {2} 這兩個變數分別代表傳入引數的第一和第二部分:

  1. $ ls -1 | parallel --max-args=2 cat {1} {2} ">" {1}_{2}.person

在上面的命令中,變數 {1} 值為 ada 或 richard (取決於你選取的任務),變數 {2}值為 lovelace 或 stallman。透過使用重定向符號(放到引號中,防止被 Bash 識別,以便 Parallel 使用),(兩個)檔案的內容被分別重定向至新檔案 ada_lovelace.person 和 richard_stallman.person

  1. $ ls -1

  2. ada

  3. ada_lovelace.person

  4. lovelace

  5. richard

  6. richard_stallman.person

  7. stallman

  8. $ cat ada_*person

  9. ada lovelace

  10. $ cat ri*person

  11. richard stallman

如果你整天處理大量幾百 MB 大小的日誌檔案,那麼(上述)並行處理文字的方法對你幫忙很大;否則,上述例子只是個用於上手的示例。

然而,這種處理方法對於很多文字處理之外的操作也有很大幫助。下麵是來自電影產業的真實案例,其中需要將一個目錄中的影片檔案和(對應的)音訊檔案進行合併。

  1. $ ls -1

  2. 12_LS_establishing-manor.avi

  3. 12_wildsound.flac

  4. 14_butler-dialogue-mixed.flac

  5. 14_MS_butler.avi

  6. ...略...

使用同樣的方法,使用下麵這個簡單命令即可並行地合併檔案:

  1. $ ls -1 | parallel --max-args=2 ffmpeg -i {1} -i {2} -vcodec copy -acodec copy {1}.mkv

簡單粗暴的方式

上述花哨的輸入輸出處理不一定對所有人的口味。如果你希望更直接一些,可以將一堆命令甩給 Parallel,然後去幹些其它事情。

首先,需要建立一個文字檔案,每行包含一個命令:

  1. $ cat jobs2run

  2. bzip2 oldstuff.tar

  3. oggenc music.flac

  4. opusenc ambiance.wav

  5. convert bigfile.tiff small.jpeg

  6. ffmepg -i foo.avi -v:b 12000k foo.mp4

  7. xsltproc --output build/tmp.fo style/dm.xsl src/tmp.xml

  8. bzip2 archive.tar

接著,將檔案傳遞給 Parallel:

  1. $ parallel --jobs 6 < jobs2run

現在檔案中對應的全部任務都在被 Parallel 執行。如果任務數量超過允許的數目(LCTT 譯註:應該是 --jobs 指定的數目或預設值),Parallel 會建立並維護一個佇列,直到任務全部完成。

更多內容

GNU Parallel 是個強大而靈活的工具,還有很多很多用例無法在本文中講述。工具的 man 頁面提供很多非常酷的例子可供你參考,包括透過 SSH 遠端執行和在 Parallel 命令中使用 Bash 函式等。YouTube[2] 上甚至有一個系列,包含大量操作演示,讓你可以直接從 GNU Parallel 團隊學習。GNU Paralle 的主要維護者還釋出了官方使用指導手冊,可以從 Lulu.com[3] 獲取。

GNU Parallel 有可能改變你完成計算的方式;即使沒有,也會至少改變你主機花在計算上的時間。馬上上手試試吧!


via: https://opensource.com/article/18/5/gnu-parallel

作者:Seth Kenlon[5] 選題:lujun9972 譯者:pinewall 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出

贊(0)

分享創造快樂