GNU Parallel是一個Linux下的工具,為了在一台或多台計算機上并行的執行計算任務,一個計算任務可以是一條shell指令或者一個以每一行做為輸入的腳本程式。通常的輸入是檔案清單、主機清單、使用者清單、URL清單或者表格清單;一個計算任務也可以是一個從管道讀取的一條指令。GNU Parallel會把輸入分塊,然後通過管道并行的執行。
如果你會使用xargs和tee指令,你會發現GNU Parallel非常易于使用,因為GNU Parallel具有與xargs一樣的選項。GNU Parallel可以替代大部分的shell循環,并且用并行的方式更快的完成計算任務。
GNU Parallel保證它的輸出與順序執行計算任務時是一樣的,這樣就可以友善的把GNU Parallel的輸出做為其它程式的輸入。
對于每一行輸入,GNU Parallel會把這一行做為參數來運作指定的指令。如果沒有給出指令,那麼這一行會被當做指令執行。多行輸入會并行的運作。GNU Parallel經常被用于替代xargs或者cat | bash。
---引用自某網絡文章
2.1 入門小例子
下面是一個小例子,幫助你了解Parallel的威力:
假設你已經知道seq指令的用途,在linux執行seq 5,會得到如下結果:
root@i-s0tsk03r:~# seq 5
1
2
3
4
5
試下執行seq 5 | parallel seq {} '>' example.{},這條指令相當于:
seq 1 > example.1
seq 2 > example.2
seq 3 > example.3
seq 4 > example.4
seq 5 > example.5
seq 5生成了一個清單,包含“1,2,3,4,5”這五個元素,通過管道符"|"傳遞給Parrallel,Parrallel中的‘{}’類似于占位符,是以就變成了上述代碼塊中的五行代碼,這裡有個小技巧,使用--dry-run選項可以列印出來Parrallel實際執行的指令:seq 5 | parallel --dry-run seq {} '>' example.{}
2.2 輸入源
GNU Parallel從輸入源中讀取資料,每個輸入源都是一個指令行,輸入源跟着‘:::’這個符号後面:
parallel echo ::: 1 2 3 4 5
指令結果:(順序可能和實際有所不同)
如果程式的輸入源是一些檔案的時候,那麼這個時候使用Parallel将非常便捷,例如:
parallel wc ::: example.*
根據上面入門小例子生成的五個檔案(example.1,example.2,example.3,example.4,example.5),使用--dry-run選項,實際執行的應該是如下指令:
wc example.1
wc example.2
wc example.3
wc example.4
wc example.5
程式運作結果是
1 1 2 example.1
2 2 4 example.2
3 3 6 example.3
4 4 8 example.4
5 5 10 example.5
如果你使用了多個:::,GNU Parallel将生成所有輸入源的組合
parallel echo ::: S M L ::: Green Red
同樣的,我們使用--dry-run指令看看實際執行的是什麼
echo S Green
echo S Red
echo M Green
echo M Red
echo L Green
echo L Red
結果當然會輸出六行,相當于輸入源3*2=6
Parallel也支援從标準輸入讀入,類似最開始seq的例子,下面是一個新的例子:
find example.* | parallel echo File
我們使用--dry-run指令看看實際執行的是什麼
echo File example.1
echo File example.2
echo File example.3
echo File example.4
echo File example.5
這個指令其實相當于:
parallel echo File ::: example.*
2.3 建構指令行
Shell指令是在:::之前的,我們可以為為指令行添加指令行選項:
parallel wc -l :: example.*
在這裡我們為wc指定了選項-l,輸出結果為(順序可能和實際有所不同):
1 example.1
2 example.2
3 example.3
4 example.4
5 example.5
上述的指令裡面可以包含多條Shell指令(或者多個程式),隻要保證每條指令之間用";"分割開來即可(shell的文法,多個指令或者程式寫在一行需要用“;”分割)
執行:parallel --dry-run echo counting lines';' wc -l ::: example.*
echo counting lines; wc -l example.1
echo counting lines; wc -l example.2
echo counting lines; wc -l example.3
echo counting lines; wc -l example.4
echo counting lines; wc -l example.5
輸出結果為(順序可能和實際有所不同):
counting lines
輸入源的值通常附加到指令後面,通過使用{},我們可以在任意地方把輸入源的值替換到{}:
parallel --dry-run echo counting {}';' wc -l {} ::: example.*
實際的指令如下:
echo counting example.1; wc -l example.1
echo counting example.2; wc -l example.2
echo counting example.3; wc -l example.3
echo counting example.4; wc -l example.4
echo counting example.5; wc -l example.5
{}替換了example.*中的每個值,實際上{}隻适用于隻有一個:::的情況,如果有多個:::,我們可以分别使用{1},{2}...{n}來替代每個:::後的輸入源,下面是一個例子:
parallel --dry-run echo count {1} in {2}';' wc {1} {2} ::: -l -c ::: example.*
#這個例子比較複雜,我們需要用dry-run看看實際到底執行了什麼,實際執行的指令如下:
echo count -l in example.1; wc -l example.1
echo count -l in example.2; wc -l example.2
echo count -l in example.3; wc -l example.3
echo count -l in example.4; wc -l example.4
echo count -l in example.5; wc -l example.5
echo count -c in example.1; wc -c example.1
echo count -c in example.2; wc -c example.2
echo count -c in example.3; wc -c example.3
echo count -c in example.4; wc -c example.4
echo count -c in example.5; wc -c example.5
先看下輸入源:
第一個輸入源:::對應的是-l -c
第二個輸入源:::對應的是example.*
是以{1}代表的是{-l -c}這個集合,{2}代表的是{example.1 example.2 example.3 example.4 example.5}這個集合,然後根據組合2*5=10,一共生成了如上的十個指令。
看到這裡,如果你考慮用parallel去改造你之前的shell中的循環,那應該是一個相當棒的主意!
2.4 輸出控制
Parallel的輸出會随着所有指令結束而被立即列印出來,這就意味着輸出的順序可能和輸入的順序不完全相同,例如:
parallel sleep {}';' echo {} done ::: 5 4 3 2 1
#這個指令一眼看上去結果應該是
5 done
4 done
3 done
2 done
1 done
#但是實際可能是
原因是什麼呢,可能是因為sleep的時候,多程序切換的順序是不固定的,如果我們想要強制有序輸出,那麼可以指定參數--keep-order/-k,這樣得到的結果就會是有序的,例
parallel -k sleep {}';' echo {} done ::: 5 4 3 2 1
輸出結果為(結果順序一定是預期的):
2.5 執行控制
如果你的任務是計算密集型的,Parallel将幫助你在每個CPU上運作一個任務,達到并行的效果。
但是有時候你希望能夠運作更多的任務,你可以通過-j/--jobs選項控制任務槽(執行任務的單元)。為parallel裡面指定--jobs參數,這裡我們可以指定parallel運作槽為2:
parallel --jobs 2 sleep {}';' echo "jobs:" {%} 'echo' {} done ::: 5 4 3 1 2
jobs: 2 echo 4 done
jobs: 1 echo 5 done
jobs: 1 echo 1 done
jobs: 2 echo 3 done
jobs: 1 echo 2 done
兩個job的槽會花1~5分鐘來完成這五個任務的處理。在這裡,我們使用了{%}來列印job的id(類似于程序id)。可以看到五個輸入被分為如下的序列:
Job slot 1:5 1 2
Job slot 2:4 3
當然了,你可以通過指定'--job 5'讓五個任務并行起來,這樣所有的五個任務會同時啟動,但是它們會在不同時刻結束。
parallel --jobs 5 sleep {}';' echo "jobs:" {%} 'echo' {} done ::: 5 4 3 1 2
輸出
jobs: 4 echo 1 done
jobs: 5 echo 2 done
jobs: 3 echo 3 done
所有的任務都是并行運作的:
Job slot 1:5
Job slot 2:4
Job slot 3:3
Job slot 4:1
Job slot 5:2
你可以傳遞'--job 0'來盡量讓任務跑滿所有CPU,而不是手動指定job的number,這樣效率會更高。
2.6 管道模式
Parallel可以通過标準輸入傳遞資料塊給指令行:
seq 1000000 | parallel --pipe wc
#這裡比較疑惑地方是加--pipe和不加該選項有什麼差別呢?
#seq 1000000 | wc,相當于生成了一個資料塊,通過标準輸入給了wc,是以結果應該是
1000000 1000000 6888896
#wc指令會列印出行數,字元數,位元組數,拿seq 11舉例子,輸出應該是
6
7
8
9
10
11
#那麼,seq 11 | wc ,應該是11,11,24,第一個11代表有11行,第二個11代表有11個字元,第三個24相當于(1到9,10拆分成1和0,11拆分成1和1,然後一共附加11個換行符)
#如果不加--pipe呢,seq 100000 | parallel wc會報錯,此時找不到wc 1到wc 100000這些指令
seq 1000000 | parallel --pipe wc的輸出(順序可能和實際有所不同):
165668 165668 1048571
149796 149796 1048572
85352 85352 597465
這樣相當于parallel内部對資料塊進行了切開,然後并行多個wc去處理一小塊資料,沒有寫代碼就輕松實作了并行的大資料檔案處理,是不是很神奇!
Tips:GNU Parallel會對大資料塊以'\n'進行拆分(即加一個換行符),拆分後的每個部分大小不會超過1MB,是以對于大資料塊的程式處理機器有幫助。
2.7 小結
相信你已經掌握了GNU Parallel的基本使用方法,在大部分的場景下,可能已經夠用了。
剩下的部分會讨論更多Parallel的使用細節,覆寫更多的使用場景。
個人聲明:本文翻譯源于GNU Parallel 2018官方文檔,很多地方加入了自己的了解,由于翻譯水準有限,可能存有勘誤。如果有涉及到版權問題,請直接郵件與我聯系:[email protected]
部落客:測試生财(一個不為996而996的測開碼農)
座右銘:專注測試開發與自動化運維,努力讀書思考寫作,為内卷的人生奠定财務自由。
内容範疇:技術提升,職場雜談,事業發展,閱讀寫作,投資理财,健康人生。
csdn:https://blog.csdn.net/ccgshigao
部落格園:https://www.cnblogs.com/qa-freeroad/
51cto:https://blog.51cto.com/14900374
微信公衆号:測試生财(定期分享獨家内容和資源)
![]()
15分鐘入門parallel