Awk

如果文件名包含 =,為什麼 awk 會停止並等待,以及如何解決這個問題?

  • October 24, 2019
awk 'processing_script_here' my=file.txt

似乎停止並無限期地等待……

這裡發生了什麼,我該如何讓它工作?

正如Chris 所說,表單的參數variablename=anything被視為變數賦值(在處理參數時執行,而不是在語句-v var=value之前執行的(較新的)參數)而不是輸入文件名。BEGIN

這在以下方面可能很有用:

awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2

您可以在其中指定不同的FS/RS每個文件。它也常用於:

awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2

哪個是更安全的版本:

awk 'NR==FNR{a[$0]; next}; {...}' file1 file2

file1(如果為空則不起作用)

但是,當您的文件名稱中包含=字元時,就會出現問題。

現在,只有當第一個剩下的=是一個有效的awk變數名時,這才是一個問題。

構成有效變數名的 inawk比 in 更嚴格sh

POSIX 要求它類似於:

[_a-zA-Z][_a-zA-Z0-9]*

只有可移植字元集的字元。但是,/usr/xpg4/bin/awkSolaris 11 至少在這方面不兼容,並且允許在變數名中使用語言環境中的任何字母字元,而不僅僅是 a-zA-Z。

因此,像x+y=fooor =baror之類的參數./foo=bar仍被視為輸入文件名,而不是賦值,因為第一個參數的剩餘部分=不是有效的變數名。類似的參數Stéphane=Chazelas.txt可能會也可能不會,取決於awk實現和語言環境。

這就是為什麼使用 awk,建議使用:

awk '...' ./*.txt

代替

awk '...' *.txt

例如,如果您不能保證txt文件名不包含=字元,則可以避免該問題。

另外,請注意,-vfoo=bar.txt如果您使用以下參數,可能會將類似的參數視為一個選項:

awk -f file.awk -vfoo=bar.txt

(也適用於1.28.0 之前awk '{code}' -vfoo=bar.txt的busybox 版本,請參閱相應的錯誤報告)。awk

同樣, using./*.txt可以解決這個問題(使用./前綴也有助於呼叫文件-,否則將其awk理解為表示標準輸入)。

這也是為什麼

#! /usr/bin/awk -f

shebangs並沒有真正起作用。雖然var=value可以通過在語句中修復ARGV值(添加./前綴)來解決這些問題:BEGIN

#! /usr/bin/awk -f
BEGIN {
 for (i = 1; i < ARGC; i++)
   if (ARGV[i] ~ /^[_[:alpha:]][_[:alnum:]]*=/)
     ARGV[i] = "./" ARGV[i]
}
# rest of awk script

這對選項沒有幫助,因為那些是由腳本awk而不是awk腳本看到的。

使用該./前綴的一個潛在外觀問題是它以 結尾,但如果您不想要它FILENAME,您可以隨時使用它來剝離它。substr(FILENAME, 3)

GNU 實現通過它的選項awk修復了所有這些問題。-E

之後-E,gawk 只需要awk腳本的路徑(其中-仍然表示標準輸入),然後只需要輸入文件路徑的列表(在那裡,甚至-沒有被特殊處理)。

它專為:

#! /usr/bin/gawk -E

shebangs,其中參數列表始終是輸入文件(請注意,您仍然可以在語句中自由編輯該ARGV列表)。BEGIN

您還可以將其用作:

gawk -e '...awk code here...' -E /dev/null *.txt

我們使用-E空腳本 ( /dev/null) 只是為了確保*.txt之後的那些始終被視為輸入文件,即使它們包含=字元。

在大多數版本的 awk 中,要執行的程序之後的參數是:

  1. 一份文件
  2. 表格的賦值x=y

由於您的文件名被解釋為 case #2,awk 仍在等待在 stdin 上讀取某些內容(因為它沒有感知到已傳遞任何文件名)。

可移植地,這種行為記錄在 POSIX 中

以下兩種類型的參數中的任何一種都可以混合使用:

  • 文件:包含要讀取的輸入的文件的路徑名,與程序中的模式集相匹配。如果未指定文件操作數,或者文件操作數為“-”,則應使用標準輸入。
  • 賦值:以可移植字元集中的下劃線或字母字元開頭的操作數(參見 IEEE Std 1003.1-2001 的基本定義卷中的表格,第 6.1 節,可移植字元集),後跟一系列下劃線、數字、和可移植字元集中的字母,後跟“=”字元,應指定變數分配而不是路徑名。

因此,可移植地,您有幾個選項(#1 可能是乾擾最小的):

  1. 使用awk ... ./my=file,它迴避了這一點,因為.它不是“可移植字元集中的下劃線或字母字元”。
  2. 使用 .將文件放在標準輸入上awk ... < my=file。但是,這不適用於多個文件。
  3. 暫時對文件進行硬連結,然後使用它。您可以執行類似的操作ln my=file my_file,然後my_file正常使用。不會執行複制,兩個文件將由相同的數據和 inode 元數據支持。使用它後,刪除創建的連結是安全的,因為對 inode 的引用數仍將大於 0。

引用自:https://unix.stackexchange.com/questions/490524