如何在通配符輸入上寫入新檔名?

如何在通配符輸入上寫入新檔名?

我在科學實習中獲得了一些使用 Inix 終端的經驗,主要使用一些實用程序,例如grep, awksed但是有一件事我已經嘗試弄清楚了一段時間,這確實會讓我更有效率與我必須做的數字運算。

我有一個腳本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 行),然後將所有列印重定向到.請注意,這有一點危險,因為擴展名不匹配將導致無法替換,從而導致覆蓋輸入檔案。因此,最好添加一個故障安全檢查來確保這一點或類似的事情。這留給讀者當作練習。 ;)FILENAMEFNRoutout != FILENAME

如果您需要一個包含文件名列表的文件,最簡單的方法是像這樣運行它

awkscript $(< /path/to/filename_list_file )

它取得 的內容filename_list_file並將其放在命令列上。

相關內容