使用 Parallel 提升 Linux 命令行執行效率

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

GNU Parallel 是一個 shell 工具,能夠並行執行任務。它能夠解析多種輸入,讓你能夠同時在多份數據上運行腳本或命令。你終於可使用所有的 CPU 了!linux

使用 Parallel 提升 Linux 命令行執行效率使用 Parallel 提升 Linux 命令行執行效率

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

安裝 GNU Parallelgithub

GNU Parallel 極可能沒有預裝在你的 Linux 或 BSD 主機上,你能夠從軟件源中安裝。以 Fedora 爲例:shell

$ sudo dnf install parallel

對於 NetBSD:函數

# pkg_add parallel

若是各類方式都不成功,請參考項目主頁[1]。工具

從串行到並行學習

正如其名稱所示,Parallel 的強大之處是以並行方式執行任務;而咱們中很多人平時仍然以串行方式運行任務。ui

當你對多個對象執行某個命令時,你實際上建立了一個任務隊列。一部分對象能夠被命令處理,剩餘的對象須要等待,直到命令處理它們。這種方式是低效的。只要數據夠多,總會造成任務隊列;但與其只使用一個任務隊列,爲什麼不使用多個更小規模的任務隊列呢?命令行

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

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

$ convert 001.jpeg 001.png
$ convert 002.jpeg 002.png
$ convert 003.jpeg 003.png
... 略 ...

對於初學者而言,這是一個不小的轉變,並且看起來是個不小的改進。再也不須要圖像界面和不斷的鼠標點擊,但仍然是費力的。

進一步改進:

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

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

使用 Parallel:

$ 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 結尾的所有文件:

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

默認狀況下,find 逐行返回搜索結果,每一個結果對應 1 行:

$ find ~/graphics -name "*jpg"
/home/seth/graphics/001.jpg
/home/seth/graphics/cat.jpg
/home/seth/graphics/penguin.jpg
/home/seth/graphics/IMG_0135.jpg

當使用管道將 find 的結果傳遞給 parallel 時,每一行中的文件路徑被視爲 parallel 命令的一個參數。另外一方面,若是你須要使用命令處理多個參數,你能夠改變隊列數據傳遞給 parallel 的方式。

下面先給出一個不那麼實際的例子,後續會作一些修改使其更加有意義。若是你安裝了 GNU Parallel,你能夠跟着這個例子操做。

假設你有 4 個文件,按照每行一個文件的方式列出,具體以下:

$ echo ada > ada ; echo lovelace > lovelace
$ echo richard > richard ; echo stallman > stallman
$ ls -1
ada
lovelace
richard
stallman

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

Parallel 默認狀況下讀取 1 個隊列對象:

$ ls -1 | parallel echo
ada
lovelace
richard
stallman

如今讓 Parallel 每一個任務使用 2 個隊列對象:

$ ls -1 | parallel --max-args=2 echo
ada lovelace
richard stallman

如今,咱們看到行已經併合並;具體而言,ls -1 的兩個查詢結果會被同時傳送給 Parallel。傳送給 Parallel 的參數涉及了任務所需的 2 個文件,但目前還只是 1 個有效參數:(對於兩個任務分別爲)「ada lovelace」 和 「richard stallman」。你真正須要的是每一個任務對應 2 個獨立的參數。

值得慶幸的是,Parallel 自己提供了上述所需的解析功能。若是你將 --max-args 設置爲 2,那麼 {1}和 {2} 這兩個變量分別表明傳入參數的第一和第二部分:

$ 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。

$ ls -1
ada
ada_lovelace.person
lovelace
richard
richard_stallman.person
stallman

$ cat ada_*person
ada lovelace
$ cat ri*person
richard stallman

若是你成天處理大量幾百 MB 大小的日誌文件,那麼(上述)並行處理文本的方法對你幫忙很大;不然,上述例子只是個用於上手的示例。

然而,這種處理方法對於不少文本處理以外的操做也有很大幫助。下面是來自電影產業的真實案例,其中須要將一個目錄中的視頻文件和(對應的)音頻文件進行合併。

$ ls -1
12_LS_establishing-manor.avi
12_wildsound.flac
14_butler-dialogue-mixed.flac
14_MS_butler.avi
...略...

使用一樣的方法,使用下面這個簡單命令便可並行地合併文件:

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

簡單粗暴的方式

上述花哨的輸入輸出處理不必定對全部人的口味。若是你但願更直接一些,能夠將一堆命令甩給 Parallel,而後去幹些其它事情。

首先,須要建立一個文本文件,每行包含一個命令:

$ cat jobs2run
bzip2 oldstuff.tar
oggenc music.flac
opusenc ambiance.wav
convert bigfile.tiff small.jpeg
ffmepg -i foo.avi -v:b 12000k foo.mp4
xsltproc --output build/tmp.fo style/dm.xsl src/tmp.xml
bzip2 archive.tar

接着,將文件傳遞給 Parallel:

$ 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[4] 選題:lujun9972[5] 譯者:pinewall[6] 校對:wxy[7]

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

原文來自:https://www.linuxprobe.com/gnu-parallel-linux.html

相關文章
相關標籤/搜索