我逐漸意識到,Unix 的命令行工具可以解決一切與文字處理有關(guān)的問題。我來講一個我遇到過的問題,以及怎樣用 Unix 命令行工具解決的。
問題
我在做我的碩士論文研究。像許多統(tǒng)計學家一樣,我需要進行許多模擬。首先我利用一些數(shù)字的種子(以保證可復現(xiàn)性)來模擬了一些數(shù)據(jù),然后在這些數(shù)據(jù)上用一個算法來估算一些東西。每次運行模擬時我都會創(chuàng)建一些文件,大概像下面這樣:
dataset-directory/0001_data.csvdataset-directory/0001_A.csv
但有時候運行會失敗。這并不是太嚴重,模擬失敗了我只需要運行另一個模擬就好了。比如,0001 這個數(shù)據(jù)我成功地運行了算法 A。因此我想在 0001 數(shù)據(jù)上使用算法 B。但我真正需要的是跟蹤哪些命令失敗了。
在許多數(shù)據(jù)上運行算法 A 之后,我得到了一大堆文件,像這樣:
dataset-directory/0001_data.csvdataset-directory/0001_A.csvdataset-directory/0002_data.csvdataset-directory/0002_A.csvdataset-directory/0003_data.csvdataset-directory/0003_A.csvdataset-directory/0004_data.csvdataset-directory/0005_data.csvdataset-directory/0005_A.csvdataset-directory/0006_data.csvdataset-directory/0006_A.csvdataset-directory/0007_data.csvdataset-directory/0007_A.csvdataset-directory/0008_data.csvdataset-directory/0009_data.csvdataset-directory/0009_A.csv...dataset-directory/0499_A.csv
通過觀察得知算法 A 在數(shù)據(jù) 0004 和 0008 上的結(jié)果不存在。那么,怎樣才能列出所有 A 沒有成功的數(shù)據(jù)呢?
我當然可以手工來做,但那樣很容易出錯,而且很麻煩。要是能寫程序完成就最好了!
解決方案
簡單地來說,那些沒有成功的就是從0001到0500的數(shù)字中去掉那些成功了的數(shù)字。而得到數(shù)字列表的最好命令就是 seq:
$>seq1012345678910
(如果只給出一個數(shù)字,那么默認序列從1開始。當然,seq 2 10也會按照你期待的那樣工作。)
現(xiàn)在,如果我能獲得成功運行的列表,就可以通過 seq 命令生成所有可能的數(shù)字,再交叉檢查兩個列表來獲得想要的結(jié)果了!
許多命令行工具只完成一項工作。例如,cut 可以獲得每行指定位置上的字符:
$>cattextLoremipsumdolorsitamet$>cattext|cut-c2-5oremolor
注意這里使用了所謂的“管道運算符” | 。我說過,許多工具只做一件事,而且把那件事做到最好。很方便的是這些工具可以組合使用。使用管道,左邊命令的結(jié)果就會變成右邊命令的輸入。注意這些命令會將輸入當做一系列行來處理,通常來說這非常方便。
我們可以把成功的文件名單列表利用管道輸入至 grep 命令,該命令可以使用正則表達式來查找。由于所有文件都以同樣長度的四個數(shù)字開始,我們可以利用正則表達式 dddd 來匹配這四個連續(xù)的數(shù)字,然后將文件末尾的 A 算法加到正則表達式中。要獲得文件列表,每行一個文件,只需執(zhí)行 ls。(盡管直接調(diào)用 ls 時并不會顯示成每行一個文件,但放在管道中時輸出就會變成這樣。)
$>lsdataset-directory|grep'dddd_A.csv'0009_A.csv0001_A.csv0002_A.csv0005_A.csv0007_A.csv0003_A.csv0006_A.csv...
出于某些原因,在使用 grep 之后這里的順序混亂了。我們可以使用 sort 命令來改正。而且由于我們只關(guān)心數(shù)字,可以使用 cut -c 1-4 來取出數(shù)字部分。
$>lsdataset-directory|grep'dddd_A.csv'|sort|cut-c1-40001000200030005000600070009...0499
這些數(shù)字跟 seq 命令的結(jié)果不太一樣,因為它們前面補了零。我們可以使用一段 Python 腳本將它們轉(zhuǎn)成整數(shù)。
#parse.pyimportsysforlineinsys.stdin:i=int(line)print(i)
現(xiàn)在將結(jié)果用管道導入這個腳本就能得到我們需要的數(shù)字:
$>lsdataset-directory|grep'dddd_A.csv'|cut-c1-4|python3parse.py1235679...499
接近目標了!現(xiàn)在需要找出怎樣才能交叉檢查這兩個數(shù)字的列表。幸運的是,有個命令叫做comm,它能檢查兩個輸入流中的公共字符。為了將上面這個命令的輸出作為輸入使用,我們可以將其求值后重定向,只需使用<(...)即可實現(xiàn):
$>comm<(ls?dataset-directory?|?grep?'dddd_A.csv'?|?cut?-c?1-4?|?python3?parse.py)?<(seq?500)????????1????????2????????3????4????????5????????6????????7????8????????9????10...????500
這個輸出不是太好理解。閱讀 comm 的手冊(運行 man comm)可以發(fā)現(xiàn),comm 會“產(chǎn)生三列的輸出,分別是:只存在于 file1 的行,只存在于 file2 的行,以及存在于兩個文件的行”。要去掉第一列(其實它是空的,因為沒有任何數(shù)字只存在于文件列表中),可以在調(diào)用 comm 時加入參數(shù)-1。我們也不關(guān)心同時存在于兩個輸入中的數(shù)字,因此還要傳遞標志-3。
$>comm-1-3<(ls?dataset-directory?|?grep?'dddd_A.csv'?|?cut?-c?1-4?|?python3?parse.py)?<(seq?500)48...500
搞定!
-
算法
+關(guān)注
關(guān)注
23文章
4612瀏覽量
92894 -
UNIX
+關(guān)注
關(guān)注
0文章
296瀏覽量
41490
原文標題:程序員,你真的會用 Unix 命令?
文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論