我在科學實習中獲得了一些使用 Inix 終端的經驗,主要使用一些實用程序,例如grep
, awk
,sed
但是有一件事我已經嘗試弄清楚了一段時間,這確實會讓我更有效率與我必須做的數字運算。
我有一個腳本run.awk
,可以對大量文字檔案執行一些操作。事實上,它將獲取該文件chloride.out
,從中提取數據並寫入chloride.cm
。
無論如何,我可以讓這個腳本根據 shell 中的初始通配符短語接收*.out
並寫入檔案嗎?*.cm
我為處理大量資料而編寫的腳本數量已經超過一百次,這真是煩人。
理想情況下,我想知道是否有一種方法可以透過 shell 為我的所有腳本執行此操作。如果它不能在 shell 或等效工具中自動化,我是否可以至少awk
以與我所描述的類似的方式自動化我的腳本?
答案1
您當然可以讓 awk 透過通配符處理多個檔案。一個建議是將 保留run.awk
為通用“函數”,它接受單個文件並生成單個輸出文件,然後從另一個腳本調用它,然後該腳本可以負責同化輸入和輸出文件。
例子
這將是一個 Bash 腳本,我們可以稱之為awk_runner.bash
.
#!/bin/bash
for ifname in *.out; do
ofname=${ifname/.out/.cm}
printf "IN: %s, OUT: %s\n" $ifname $ofname
printf "running run.awk with %s & %s\n\n" $ifname $ofname
run.awk $ifname $ofname
done
樣品運行
我創建了一個範例目錄,其中包含一些測試文件。
$ touch file{1..4}.out
這導致生成了 4 個檔案:
$ ls -1
file1.out
file2.out
file3.out
file4.out
現在我們運行我們的腳本:
$ ./awk_runner.bash
IN: file1.out, OUT: file1.cm
running run.awk with file1.out & file1.cm
IN: file2.out, OUT: file2.cm
running run.awk with file2.out & file2.cm
IN: file3.out, OUT: file3.cm
running run.awk with file3.out & file3.cm
IN: file4.out, OUT: file4.cm
running run.awk with file4.out & file4.cm
在以“running...”開頭的每一行之後,我們的腳本可以從這裡運行。
列表中的文件
假設我們不使用通配符,*.out
而是使用一個包含文件名稱清單的文件,例如:
$ cat filelist.txt
file1.out
file2.out
file3.out
file4.out
我們可以使用腳本的修改版本,它將使用循環while
而不是for
循環。現在我們將這個腳本的變體稱為awk_file_runner.bash
:
#!/bin/bash
while read ifname; do
ofname=${ifname/.out/.cm}
printf "IN: %s, OUT: %s\n" $ifname $ofname
printf "running run.awk with %s & %s\n\n" $ifname $ofname
run.awk $ifname $ofname
done < filelist.txt
此版本的腳本從檔案讀取輸入filelist.txt
:
done < filelist.txt
然後,對於循環的每一輪while
,我們使用read
命令從輸入檔中讀取一行。
while read ifname; do
然後,它以與第一個腳本相同的方式執行所有操作,其中它將在循環遍歷文件的每一行時運行awk
腳本。run.awk
答案2
您可以直接在 awk 中執行此操作,而不是編寫 shell 包裝器並為您處理的每個檔案產生一個新的 awk 實例。如果您已有 awk 腳本,則可以使用 FILENAME 變數存取目前檔案。因此,如果您執行awk 'some commands' file1 file2
,您可以使用 FILENAME 判斷您正在使用 file1 還是 file2。您也可以在 awk 中使用>
on print
/ 。printf
所以,如果你有一個像這樣的 awk 腳本
/pattern/{ print $1,$3 }
你可以輕鬆做到
/pattern/{ print $1,$3 > FILENAME".processed" }
或用來FNR=1
告訴您何時位於新檔案中,並建立一個變數來對檔案名稱進行更複雜的操作。就像用 替換.in
擴展名一樣.out
,如
sauer@humpy:/tmp$ grep . file*.in
file1.in:a
file1.in:b
file2.in:c
sauer@humpy:/tmp$ awk 'FNR=1{out=FILENAME;sub("\.in$",".out",out)} {print "processed"$0 > out}' file*.in
sauer@humpy:/tmp$ grep . file*.out
file1.out:processeda
file1.out:processedb
file2.out:processedc
我用來grep .
在此處顯示文件名和多個文件的內容,這也是一個有趣的技巧。但重要的是,將變數的值設為當更改為 1 時out
的修改版本(因此我們位於文件的第 1 行),然後將所有列印重定向到.請注意,這有一點危險,因為擴展名不匹配將導致無法替換,從而導致覆蓋輸入檔案。因此,最好添加一個故障安全檢查來確保這一點或類似的事情。這留給讀者當作練習。 ;)FILENAME
FNR
out
out != FILENAME
如果您需要一個包含文件名列表的文件,最簡單的方法是像這樣運行它
awkscript $(< /path/to/filename_list_file )
它取得 的內容filename_list_file
並將其放在命令列上。