介紹如何在 Bash 指令稿中使用 parallel
指令工具平行化執行程式。
安裝 parallel
在 Ubuntu Linux 中若要安裝 parallel
,可以透過 apt
來安裝:
# 安裝 parallel 套件
sudo apt install parallel
基礎語法
parallel
可以用來將大量的指令或資料拆分之後,使用多 CPU 的方式平行處理,加快執行的速度。
以下是一個簡單的範例,先以 ls
指令產生文字檔的清單,交給 parallel
之後,以行為單位將資料切開,以平行的方式進行處理:
# 以 parallel 平行處理 ls *.txt | parallel echo "Got file:" {}
Got file: A.txt Got file: B.txt Got file: C.txt
這裡的 {}
在執行時會自動被替換為讀入的每一行資料。
xargs
指令也可以達到類似的效果:
# 以 xargs 平行處理(最多使用 4 個行程) ls *.txt | xargs -I{} -n1 -P4 echo "Got file:" {}
Got file: A.txt Got file: B.txt Got file: C.txt
清單來源
除了從標準輸入(standard input)讀取資料之外,也可以從命令列參數中讀取。
# 從命令列參數中讀取資料清單 parallel echo "Got file:" {} ::: A.txt B.txt C.txt
Got file: A.txt Got file: B.txt Got file: C.txt
這裡的 :::
是 parallel
的特殊參數,其代表隨後的參數都是要輸入的資料。
如果資料清單存在於檔案中,也可以直接從檔案中讀取。假設 list.txt
的檔案內容如下:
Got file: A.txt Got file: B.txt Got file: C.txt
我們可以透過 -a
參數來指定輸入資料的檔案:
# 從檔案中讀取資料清單 parallel -a list.txt echo "Got file:" {}
Got file: A.txt Got file: B.txt Got file: C.txt
以下兩種寫法也可以從檔案中取得輸入的資料,作用都相同:
# 從檔案中讀取資料清單 parallel echo "Got file:" {} < list.txt # 從檔案中讀取資料清單 cat list.txt | parallel echo "Got file:" {}
檔案路徑處理
在 parallel
所指定執行的指令中,可以包含各種代碼,用來替換成輸入的每一行原始資料或經過處理後的資料:
代碼 | 解釋 |
{} |
輸入的每一行原始資料。 |
{.} |
除去副檔名的路徑資料。 |
{/} |
除去路徑的目錄,只包含檔案名稱。 |
{//} |
除去檔案名稱,只包含目錄路徑。 |
{/.} |
除去路徑與副檔名,只包含檔案基本名稱。 |
這些代碼在處理檔案格式轉換時特別有用,例如將 wav 檔案轉為 mp3 檔案:
# 將 wav 檔案轉為 mp3 檔案 parallel lame {} -o {.}.mp3 ::: *.wav
這樣可讓轉換出來的檔案名稱維持不變,只有副檔從 .wav
變成 .mp3
。
最高行程數
parallel
預設會偵測機器的 CPU 核心數,同時執行跟 CPU 核心相同數量的行程,讓每顆 CPU 都被充分利用。
若要自行指定平行化執行時最高的行程數量,可以使用 -j
參數來指定:
# 最高同時執行 4 個行程 ls *.txt | parallel -j4 echo "Got file:" {}
基本範例
平行化建立多個檔案:
# 建立 00.txt, 01.txt, ..., 10.txt seq -w 0 10 | parallel touch {}.txt
平行化轉換所有 JPG 圖檔,產生縮圖:
# 平行轉換所有 JPG 圖檔,產生縮圖 find . -name '.jpg' | parallel convert -geometry 100 {} {}_thumb.jpg
以平行化的方式,對目前目錄與子目錄之下的所有 HTML 檔案進行最佳的 gzip 壓縮:
# 以平行方式壓縮目前目錄與子目錄下所有 HTML 檔案 find . -type f -name '*.html' -print | parallel gzip --best
如果只是要對目錄之下的 HTML 檔案進行 gzip 壓縮,可以執行:
# 以平行方式壓縮目前目錄下的 HTML 檔案 parallel gzip --best ::: *.html
進階範例
平行下載多 URL 網址,列出下載失敗的 URL:
# 平行下載多 URL 網址,列出下載失敗的 URL cat urlfile.txt | parallel "wget {} 2>/dev/null || grep -n {} urlfile.txt"
依據每一個 ZIP 壓縮檔名稱建立目錄,並解壓縮至其中:
# 依據每一個 ZIP 壓縮檔名稱建立目錄,並解壓縮至其中 parallel 'mkdir {.}; cd {.}; unzip ../{}' ::: *.zip
將所有 gzip 壓縮檔解壓縮後重新以 bzip2 壓縮:
# 將所有 GZIP 壓縮檔解壓縮後重新以 BZIP2 壓縮 parallel "zcat {} | bzip2 >{.}.bz2 && rm {}" ::: *.gz
監看 my_dir
目錄,及時平行處理所有變動的檔案:
# 監看 my_dir 目錄,平行處理變動檔案 inotifywait -q -m -r -e MOVED_TO -e CLOSE_WRITE --format %w%f my_dir |\ parallel -u echo {}