Linux

Bash 程式設計教學與範例:GNU Parallel 平行執行程式

介紹如何在 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 {}

參考資料

Share
Published by
Office Guide
Tags: Bash

Recent Posts

Python 使用 PyAutoGUI 自動操作滑鼠與鍵盤

本篇介紹如何在 Python ...

1 年 ago

Ubuntu Linux 以 WireGuard 架設 VPN 伺服器教學與範例

本篇介紹如何在 Ubuntu ...

1 年 ago

Linux 網路設定 ip 指令用法教學與範例

本篇介紹如何在 Linux 系...

1 年 ago

Linux 以 Cryptsetup、LUKS 加密 USB 隨身碟教學與範例

介紹如何在 Linux 系統中...

1 年 ago