如果文件名包含 =,為什麼 awk 會停止並等待,以及如何解決這個問題?
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
變數名時,這才是一個問題。構成有效變數名的 in
awk
比 in 更嚴格sh
。POSIX 要求它類似於:
[_a-zA-Z][_a-zA-Z0-9]*
只有可移植字元集的字元。但是,
/usr/xpg4/bin/awk
Solaris 11 至少在這方面不兼容,並且允許在變數名中使用語言環境中的任何字母字元,而不僅僅是 a-zA-Z。因此,像
x+y=foo
or=bar
or之類的參數./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 中,要執行的程序之後的參數是:
- 一份文件
- 表格的賦值
x=y
由於您的文件名被解釋為 case #2,awk 仍在等待在 stdin 上讀取某些內容(因為它沒有感知到已傳遞任何文件名)。
可移植地,這種行為記錄在 POSIX 中:
以下兩種類型的參數中的任何一種都可以混合使用:
- 文件:包含要讀取的輸入的文件的路徑名,與程序中的模式集相匹配。如果未指定文件操作數,或者文件操作數為“-”,則應使用標準輸入。
- 賦值:以可移植字元集中的下劃線或字母字元開頭的操作數(參見 IEEE Std 1003.1-2001 的基本定義卷中的表格,第 6.1 節,可移植字元集),後跟一系列下劃線、數字、和可移植字元集中的字母,後跟“=”字元,應指定變數分配而不是路徑名。
因此,可移植地,您有幾個選項(#1 可能是乾擾最小的):
- 使用
awk ... ./my=file
,它迴避了這一點,因為.
它不是“可移植字元集中的下劃線或字母字元”。- 使用 .將文件放在標準輸入上
awk ... < my=file
。但是,這不適用於多個文件。- 暫時對文件進行硬連結,然後使用它。您可以執行類似的操作
ln my=file my_file
,然後my_file
正常使用。不會執行複制,兩個文件將由相同的數據和 inode 元數據支持。使用它後,刪除創建的連結是安全的,因為對 inode 的引用數仍將大於 0。