在我的 Perl 腳本中,我想處理來自任STDIN一檔案或給定檔案(如果指定)的行,這與 Linux/UNIX 命令列實用程式一樣。
為此,我的腳本中有以下部分(為帖子簡化):
use strict;
use warnings;
my $in = \*STDIN;
open $in, '<', $ARGV[0] or die if (defined $ARGV[0]);
print while (<$in>);
本質上,我定義$in為對STDINtypeglob 的參考,所以通常,如果沒有指定引數,腳本會print為<STDIN>. 到現在為止還挺好。
但是,如果$ARGV[0]已定義,我想從中讀取行。這就是第二條有意義的線路所聲稱要做的。但是,使用引數運行時似乎沒有處理任何行。
我注意到,在我有條件地呼叫 , 之后open,$in即使我期望它會改變;
my $in = \*STDIN;
print $in, "\n";
open $in, '<', $ARGV[0] or die if (defined $ARGV[0]);
print $in, "\n";
產量
GLOB(0xaa08b2f4f28)
GLOB(0xaa08b2f4f28)
即使$ARGV[0]定義了。open當傳遞的第一個變數已經參考檔案句柄時不起作用?
相關檔案確實包括以下內容
關于檔案句柄
open 的第一個引數,在這個參考中標記為 FILEHANDLE,通常是一個標量變數。(存在例外,在下面的“其他注意事項”中進行了描述。)如果對 open 的呼叫成功,則作為 FILEHANDLE 提供的運算式將被分配一個打開的檔案句柄。該檔案句柄提供對指定外部檔案的內部參考,方便地存盤在 Perl 變數中,并為諸如讀取和寫入之類的 I/O 操作做好準備。
僅基于這一點,我不明白為什么我的代碼不起作用。
uj5u.com熱心網友回復:
這正是空檔案句柄 <>所做的
輸入來自
<>標準輸入或命令列中列出的每個檔案。
所以你只需要
while (<>) {
...
}
(請參閱檔案的其余部分)
另一種在某些情況下更安全的選擇是使用雙菱形支架
while (<<>>) { }
在 while 內使用雙尖括號會導致 open 使用三引數形式(第二個引數為
<),因此所有引數ARGV都被視為文字檔案名(包括“-”)。(注意,為方便起見,如果使用<<>>and if@ARGV為空,它仍然會從標準輸入中讀取。)
(再次,請參閱檔案的其余部分)
對于問題的第二部分,并在評論中進行討論之后,值得注意的是my $in = \*STDIN創建了一個別名STDIN(不是副本);看到這個帖子。然后打開一個帶有這樣的標量的檔案(之前已經分配了對 typeglob 的參考),因為 filehandle 只會重定向原始的 typeglob。因此,一旦我們open將$in檔案句柄STDIN連接到該檔案,就在這里。
這很容易檢查
perl -wE'
$in = \*STDIN;
say "\$in: $$in"; #--> *main::STDIN
print while <$in>; # type input, then Ctrl-D
open $in, "<", $ARGV[0] or die $!;
say "\$in is: $$in"; #--> *main::STDIN
print while <$in>; # but prints the file
seek $in, 0, 0;
print while <STDIN>; # prints the file
' file
在我們輸入一些輸入(列印回來)和 Ctrl-D 后,在open-ing 檔案之后,檔案句柄顯示仍然存在STDIN,但它確實列印出該檔案。然后列印STDIN仍然列印檔案。
已STDIN重新連接open到檔案;找回它并不簡單。因此,如果要真正STDIN與詞匯相關聯,那么最好欺騙它。請參閱檔案和鏈接的帖子。
至于直接的問題——是的,可以通過open-ing 重新分配檔案句柄。
但是... or die if ...語法是錯誤的,因為不能像這樣鏈接條件。
但是,我無法重現顯示的行為,因為您的代碼實際上對我有用(在 Linux 上的 5.16 和 5.30 上)。那么我最好的猜測是這樣的代碼會導致“未定義的行為”,我們會得到不可預測和不一致的行為。
考慮
E1 or E2 if E3;
其中Es 代表運算式。(這是為open(...) or die($!) if COND;)
應該if E3適用于什么——單獨的E2還是整體的E1 or E2?沒有辦法說,那么一個人可能會得到什么是可怕的“未定義行為”(UB)——它可能實際上作業,有時/在某些條件下/在某些系統上,或者可能發生任何其他事情。
現在,可能還有更多內容:E2 if E3 不能成為條件的一部分,因此將其全部解釋為E1 or (E2 if E3);直接非法語法,因此在我的程式中,該陳述句可能被解釋為
(E1 or E2) if E3;
這很好(并且按預期作業,因為它發生了)。但是,原始陳述句仍然必須是 UB,并且在 OP 的系統上它不起作用。
因此,如果您確實需要至少有一個檔案句柄,可以通過添加括號來解決這個問題
(open $in, '<', $ARGV[0] or die $!) if defined $ARGV[0];
但我建議撰寫一個漂亮且可讀的測驗,而不是將其塞進一個陳述句中(并從一STDIN開始就重復)。
uj5u.com熱心網友回復:
您想使用神奇的ARGV檔案句柄,它完全符合您的要求。
以下是從中讀取的最安全的方法:
while (<<>>) {
...
}
你想要這樣的事情:
my $in_fh;
if ( @ARGV ) {
open( $in_fh, "<", $ARGV[0] )
or die( "Can't open `$ARGV[0]`: $!\n" );
} else {
$in_fh = \*STDIN;
}
while (<$in_fh>) {
...
}
但是,與 unix 工具不同的是,這僅從提供的第一個檔案中讀取。使用第一個解決方案從提供的每個檔案中讀取。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/475693.html
上一篇:Perl正則運算式-僅列印修改的行(如sed-n's///p')
下一篇:Perl動態/符號函式參考
