好用的parallel命令
簡(jiǎn)介
有時(shí),我們需要處理一批數(shù)據(jù),使用while循環(huán)是個(gè)不錯(cuò)的想法,但while循環(huán)中的命令是一個(gè)一個(gè)執(zhí)行的,如果要批量處理的數(shù)據(jù)很多,執(zhí)行時(shí)間就會(huì)很長(zhǎng),而parallel可以讓命令并行執(zhí)行,從而縮短命令執(zhí)行時(shí)間。
下面,我們先用ncat來(lái)模擬一個(gè)處理數(shù)據(jù)的接口。
模擬接口
ncat -lk 8088 -c 'sleep 1;printf "HTTP/1.1 200 OK\r\nContent-Type: plain/text\r\nContent-Length: 3\r\n\r\nok\n"' 復(fù)制代碼此接口直接睡眠1秒,然后返回一個(gè)ok,表示數(shù)據(jù)處理成功。
調(diào)用接口
curl -X POST http://localhost:8088/user/add -d '{"user_id": 1, "user_name":"u1"}' 復(fù)制代碼測(cè)試數(shù)據(jù)
假設(shè)有10條數(shù)據(jù),在data.txt中,如下:
1 u1 2 u2 3 u3 ... 復(fù)制代碼使用while循環(huán)處理
$ time while read -r -a line; docurl -X POST http://localhost:8088/user/add -d '{"user_id": '${line[0]}', "user_name":"'${line[1]}'"}'done < data.txt ok ok ok ok ok ok ok ok ok okreal 0m10.276s user 0m0.094s sys 0m0.096s 復(fù)制代碼使用while循環(huán)處理,其中time命令用來(lái)計(jì)時(shí),real表示while命令的執(zhí)行時(shí)間,可以看到,處理完10條數(shù)據(jù)花了約10秒,接下來(lái)我們使用parallel并發(fā)執(zhí)行。
使用parallel并發(fā)執(zhí)行
$ time cat data.txt | parallel -j10 -C '\s+' curl -s -X POST http://localhost:8088/user/add -d \'{\"user_id\": {1}, \"user_name\":\"{2}\"}\' ok ok ok ok ok ok ok ok ok okreal 0m1.205s user 0m0.203s sys 0m0.060s 復(fù)制代碼使用parallel命令并發(fā)執(zhí)行curl,-j10表示最多10個(gè)并發(fā)進(jìn)程,-C '\s+' 表示使用空白來(lái)拆分每行(注:\s+是表示空白的正則表達(dá)式),這樣就可以使用{1}表示第1列,{2}表示第2列了,如我們所預(yù)期的,10條數(shù)據(jù)使用10個(gè)并發(fā),處理完約1秒。
有用的--tag選項(xiàng)
上例的接口很簡(jiǎn)單,直接返回ok,但在有大量數(shù)據(jù)需要處理時(shí),有可能出現(xiàn)部分?jǐn)?shù)據(jù)處理失敗,像上面的執(zhí)行結(jié)果中,就很難知道是哪些數(shù)據(jù)處理失敗了,還好parallel提供了--tag選項(xiàng),可以將處理的數(shù)據(jù)與執(zhí)行結(jié)果都打印出來(lái),如下:
$ cat data.txt | parallel -j10 -C '\s+' --tag curl -s -X POST http://localhost:8088/user/add -d \'{\"user_id\": {1}, \"user_name\":\"{2}\"}\' 1 u1 ok 2 u2 ok 4 u4 ok 3 u3 ok 5 u5 ok 6 u6 ok 7 u7 ok 8 u8 ok 9 u9 ok 10 u10 ok 復(fù)制代碼這樣,什么數(shù)據(jù)執(zhí)行對(duì)應(yīng)什么結(jié)果,就一目了然了。
查看進(jìn)度
如果有大量數(shù)據(jù)需要處理,處理時(shí)能直觀的看到一個(gè)進(jìn)度就再好不過(guò)了,parallel提供了3個(gè)查看進(jìn)度的選項(xiàng),--bar、--progress和--eta,一般使用--bar、--progress即可。
其中--bar適合待處理數(shù)據(jù)量確定的場(chǎng)景,因?yàn)閜arallel需要讀取所有數(shù)據(jù)后才能根據(jù)數(shù)據(jù)總量計(jì)算進(jìn)度條。
而--progress適合待處理數(shù)據(jù)量未知的場(chǎng)景,只能看到已經(jīng)處理了多少條數(shù)據(jù),如下:
--joblog與--resume-failed選項(xiàng)
相信當(dāng)你使用腳本處理有一定數(shù)據(jù)量的數(shù)據(jù)時(shí),一定會(huì)遇到數(shù)據(jù)偶爾處理失敗的情況(由于網(wǎng)絡(luò)不穩(wěn)定),這時(shí)你需要將處理失敗的數(shù)據(jù)再次找出來(lái),然后再次處理,過(guò)程還是挺麻煩的。
好在parallel命令已經(jīng)考慮到了這種場(chǎng)景,并提供了--joblog與--resume-failed選項(xiàng),當(dāng)有失敗產(chǎn)生時(shí),你只需要再次執(zhí)行整個(gè)命令行即可。
--semaphore選項(xiàng)
parallel既然提供了并發(fā),那么必然會(huì)遇到并發(fā)沖突問(wèn)題,比如sed命令就不支持并發(fā)的修改同一文件,不過(guò)parallel已經(jīng)提供了--semaphore選項(xiàng)來(lái)解決這個(gè)問(wèn)題了。
如下,其中sem是parallel --semaphore的別名,與其是等價(jià)的。
這里的邏輯是,每處理成功data.txt中的一條數(shù)據(jù),就使用sed將data.txt中的那行數(shù)據(jù)末尾加一個(gè)ok,表示執(zhí)行成功,然后在前面使用grep找不包含ok的數(shù)據(jù),就達(dá)到了命令每次都處理未處理或處理失敗數(shù)據(jù)的邏輯。而sem -j1保護(hù)了sed,避免sed命令并發(fā)執(zhí)行。
與mysql結(jié)合使用
parallel還可以和mysql結(jié)合使用,將任務(wù)導(dǎo)入mysql中或是執(zhí)行mysql中的任務(wù),如下:
# 1.將任務(wù)數(shù)據(jù)導(dǎo)入到pardb庫(kù)的paralleljobs表中,pardb庫(kù)需要事先自行創(chuàng)建 cat data.txt |parallel --sqlmaster 'sql:mysql://user:pass@localhost:3306/pardb/paralleljobs'# 2.執(zhí)行paralleljobs表中待處理任務(wù),Exitval=-1000為待處理任務(wù) function deal_data(){p=($*)res=$(curl -s -X POST http://localhost:8088/user/add -d '{"user_id": '${p[0]}', "user_name":"'${p[1]}'"}')echo "$res"[[ "$res" == "true" ]] && return 0 || return 1 } export -f deal_dataparallel --sqlworker 'sql:mysql://user:pass@localhost:3306/pardb/paralleljobs' --tag deal_data 復(fù)制代碼處理csv數(shù)據(jù)
parallel命令還能很方便的處理csv文件數(shù)據(jù),比如將data.txt改為data.csv,如下:
$ cat data.csv user_id,user_name 1,u1 2,u2 3,u3 ... # 使用--header : 選項(xiàng)來(lái)讀取csv的表頭,然后就可以{user_id},{user_name}來(lái)占位命令參數(shù)了 $ cat data.csv | parallel --header : -C ',' --tag curl -s -X POST http://localhost:8088/user/add -d \'{\"user_id\": {user_id}, \"user_name\":\"{user_name}\"}\' 復(fù)制代碼--pipe選項(xiàng)
有很多文本處理命令,并不從參數(shù)中獲取數(shù)據(jù),而是從標(biāo)準(zhǔn)輸入中獲取,比如paste,通過(guò)指定--pipe選項(xiàng),能將數(shù)據(jù)傳入到待執(zhí)行命令的輸入流中去。
# 比如我想把data.csv變成data.json,且每3條數(shù)據(jù)聚合成一個(gè)json數(shù)組,如下: $ cat data.csv | parallel --header : -C ',' echo \'{\"user_id\": {user_id}, \"user_name\":\"{user_name}\"}\' | parallel -N3 --pipe paste -s -d, | sed -e 's/^/\[/' -e 's/$/]/' [{"user_id": 7, "user_name":"u7"},{"user_id": 8, "user_name":"u8"},{"user_id": 9, "user_name":"u9"}] [{"user_id": 1, "user_name":"u1"},{"user_id": 2, "user_name":"u2"},{"user_id": 3, "user_name":"u3"}] [{"user_id": 4, "user_name":"u4"},{"user_id": 5, "user_name":"u5"},{"user_id": 6, "user_name":"u6"}] [{"user_id": 10, "user_name":"u10"}] 復(fù)制代碼第一個(gè)parallel將每條數(shù)據(jù)變成了{(lán)"user_id": 1, "user_name":"u1"}形式。
第二個(gè)parallel將每3個(gè)json傳給paste的輸入流,然后paste使用逗號(hào)將它們連接起來(lái)。
每三個(gè)sed給首尾加上[],即成為了需要的數(shù)據(jù)格式。
與tmux結(jié)合使用
parallel提供了--tmuxpane,使得可以實(shí)現(xiàn)在tmux的多個(gè)panel中執(zhí)行命令,這非常適合用來(lái)觀察一些監(jiān)控命令的結(jié)果,比如查看每臺(tái)主機(jī)的網(wǎng)絡(luò)情況。
# 使用ping同時(shí)監(jiān)控簡(jiǎn)書(shū)、百度、知乎的網(wǎng)絡(luò)情況,注意,必須要加--delay選項(xiàng) $ printf "www.jianshu.com\nwww.jianshu.com\nwww.baidu.com\nwww.zhihu.com"|parallel -j0 --delay 1 --tmuxpane ping {0} See output with: tmux -S /tmp/tmsOHGXM attach # 查看tmux面板 $ tmux -S /tmp/tmsOHGXM attach 復(fù)制代碼如下,為tmux面板的內(nèi)容,你將能直觀的看到4臺(tái)機(jī)器的ping實(shí)時(shí)結(jié)果。
總結(jié)
如果你經(jīng)常使用shell來(lái)幫助你處理各種問(wèn)題,我想parallel命令就非常適合你,它真的太強(qiáng)大太方便了。
作者:打碼日記
總結(jié)
以上是生活随笔為你收集整理的好用的parallel命令的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: angular页面间传递参数
- 下一篇: BEC高级备考