將內部 FILEHANDLE 與 EXPR 指定的外部檔案關聯起來。該檔案句柄會允許您在該檔案上執行 I/O 作業,例如從檔案中讀取或寫入檔案中。
您可以指定外部命令(加上選用的引數清單)或純量參考,而不是檔案名稱,以分別在命令或記憶體中純量上開啟檔案句柄。
以下是 open
的完整參考。若要更溫和地瞭解 open
的基礎知識,請參閱 perlopentut 手冊頁面。
最常使用三個引數呼叫 open
:必要的 FILEHANDLE(通常是空的純量變數),接著是 MODE(通常是描述檔案句柄將使用的 I/O 模式的字面值),然後是新檔案句柄將引用的檔案名稱。
從檔案讀取
open(my $fh, "<", "input.txt")
or die "Can't open < input.txt: $!";
# Process every line in input.txt
while (my $line = readline($fh)) {
#
# ... do something interesting with $line here ...
#
}
或寫入檔案
open(my $fh, ">", "output.txt")
or die "Can't open > output.txt: $!";
print $fh "This line gets printed into output.txt.\n";
如需這些常見檔案句柄操作的摘要,請參閱 perlintro 中的「檔案和 I/O」。
傳遞給 open
的第一個引數,在此參考中標示為 FILEHANDLE,通常是純量變數。(例外情況說明如下文的「其他考量」中。)如果呼叫 open
成功,則提供為 FILEHANDLE 的表達式將會指派一個開啟的檔案句柄。該檔案句柄提供對指定外部檔案的內部參考,方便儲存在 Perl 變數中,並準備好執行 I/O 操作,例如讀取和寫入。
使用三個或更多引數呼叫 open
時,第二個引數(在此標示為 MODE)定義開啟模式。MODE 通常是包含特殊字元的文字字串,用來定義要建立的檔案句柄的預期 I/O 角色:它是唯讀的,還是可讀寫的,等等。
如果 MODE 是 <
,則檔案會開啟為輸入(唯讀)。如果 MODE 是 >
,則檔案會開啟為輸出,現有的檔案會先被截斷(「覆寫」),而不存在的檔案則會新建立。如果 MODE 是 >>
,則檔案會開啟為追加,並在必要時建立。
您可以在 >
或 <
前面加上 +
,以表示您想要對檔案同時擁有讀取和寫入權限;因此,+<
幾乎總是優先用於讀寫更新,而 +>
模式會先覆寫檔案。您通常無法對文字檔案使用任何讀寫模式來更新,因為它們具有變長記錄。請參閱 perlrun 中的 -i 開關,以取得更好的方法。檔案建立時具有 0666
權限,並修改為程序的 umask
值。
這些不同的前置詞對應於 fopen(3) 的 r
、r+
、w
、w+
、a
和 a+
模式。
不同模式運作的更多範例
# Open a file for concatenation
open(my $log, ">>", "/usr/spool/news/twitlog")
or warn "Couldn't open log file; discarding input";
# Open a file for reading and writing
open(my $dbase, "+<", "dbase.mine")
or die "Can't open 'dbase.mine' for update: $!";
Open 在成功時傳回非零值,否則傳回未定義值。如果 open
涉及管道,傳回值會是子程序的 pid。
開啟檔案時,如果要求失敗,繼續執行通常不是一個好主意,因此 open
經常與 die
一起使用。即使您希望程式碼在開啟失敗時執行 die
以外的操作,您仍應始終檢查開啟檔案的傳回值。
您可以使用 open 的三個引數形式來指定 I/O 層(有時稱為「規範」),以套用至新的檔案句柄。這些層會影響輸入和輸出的處理方式(請參閱 open 和 PerlIO 以取得更多詳細資料)。例如
# loads PerlIO::encoding automatically
open(my $fh, "<:encoding(UTF-8)", $filename)
|| die "Can't open UTF-8 encoded $filename: $!";
這會開啟包含 Unicode 字元的 UTF8 編碼檔案;請參閱 perluniintro。請注意,如果在三個引數形式中指定層,則會忽略儲存在 ${^OPEN}
中的預設層(通常由 open 規範或開關 -CioD
設定)。如果您指定冒號而後面沒有名稱,這些層也會被忽略。在這種情況下,會使用作業系統的預設層(Unix 上為 :raw,Windows 上為 :crlf)。
在某些系統上(一般來說,是 DOS 和 Windows 系統),當您不使用文字檔案時,binmode
是必要的。為了移植性,最好在適當的時候使用它,在不適當的時候不要使用它。此外,人們可以將其 I/O 設定為預設 UTF8 編碼的 Unicode,而不是位元組。
undef
作為暫存檔作為特殊情況,三個參數形式具有讀取/寫入模式,第三個參數為 undef
open(my $tmp, "+>", undef) or die ...
開啟一個檔案句柄,指向一個新建立的空匿名暫存檔。(這發生在任何模式下,這使得 +>
成為唯一有用且明智的模式。)您需要 seek
來進行讀取。
您可以直接將檔案句柄開啟到 Perl 純量,而不是程式外部的檔案或其他資源。為此,請提供對該純量的參照,作為 open
的第三個參數,如下所示
open(my $memory, ">", \$var)
or die "Can't open memory file: $!";
print $memory "foo!\n"; # output will appear in $var
要將 STDOUT
或 STDERR
(重新) 開啟為記憶體中的檔案,請先關閉它
close STDOUT;
open(STDOUT, ">", \$variable)
or die "Can't open STDOUT: $!";
記憶體中檔案的純量被視為八位元組字串:除非檔案是以截斷方式開啟,否則純量可能不包含任何超過 0xFF 的碼點。
開啟記憶體中的檔案可能會因各種原因而失敗。與任何其他 open
一樣,請檢查回傳值是否成功。
技術說明:此功能僅在 Perl 使用 PerlIO 建立時才有效 -- 預設值,除了較舊的(5.16 之前的)Perl 安裝,這些安裝已設定為不包含它(例如透過 Configure -Uuseperlio
)。您可以透過執行 perl -V:useperlio
來查看您的 Perl 是否使用 PerlIO 建立。如果它顯示 'define'
,表示您有 PerlIO;否則,您沒有。
請參閱 perliol 以取得 PerlIO 的詳細資訊。
如果 MODE 為 |-
,則檔案名稱會被解釋為一個指令,輸出會被導向到該指令;如果 MODE 為 -|
,則檔案名稱會被解釋為一個指令,該指令會將輸出導向給我們。在兩個引數(和一個引數)的形式中,應該用指令取代破折號 (-
)。請參閱 perlipc 中的「使用 open() 進行 IPC」,以取得更多相關範例。(您無法對同時輸入和輸出的指令執行 open
,但請參閱 IPC::Open2、IPC::Open3 和 perlipc 中的「與另一個程序進行雙向通訊」,以取得替代方案。)
open(my $article_fh, "-|", "caesar <$article") # decrypt
# article
or die "Can't start caesar: $!";
open(my $article_fh, "caesar <$article |") # ditto
or die "Can't start caesar: $!";
open(my $out_fh, "|-", "sort >Tmp$$") # $$ is our process id
or die "Can't start sort: $!";
在執行 pipe 開啟的表單中,如果指定了 LIST(指令名稱後的額外引數),則當平台支援時,LIST 會成為呼叫指令的引數。對於非 pipe 模式,open
在超過三個引數的情況下的意義尚未定義,但實驗性的「層」可能會賦予額外的 LIST 引數意義。
如果您對指令 -
開啟 pipe(亦即,使用 open
的一個或兩個引數形式指定 |-
或 -|
),則會執行隱式的 fork
,因此 open
會傳回兩次:在父程序中,它會傳回子程序的 pid,而在子程序中,它會傳回(已定義的)0
。使用 defined($pid)
或 //
來判斷開啟是否成功。
例如,使用下列任一方式
my $child_pid = open(my $from_kid, "-|")
// die "Can't fork: $!";
或
my $child_pid = open(my $to_kid, "|-")
// die "Can't fork: $!";
接著
if ($child_pid) {
# am the parent:
# either write $to_kid or else read $from_kid
...
waitpid $child_pid, 0;
} else {
# am the child; use STDIN/STDOUT normally
...
exit;
}
檔案控制代碼對父代碼執行正常,但對該檔案控制代碼的 I/O 會從子程序的 STDOUT/STDIN 導向或導向子程序的 STDOUT/STDIN。在子程序中,檔案控制代碼並未開啟,I/O 會從/到新的 STDOUT/STDIN 發生。通常,當您想要對 pipe 指令的執行方式進行更嚴格的控制時,會使用這種方式,例如在執行 setuid 時,您不希望必須掃描 shell 指令的元字元。
下列區塊或多或少是等效的
open(my $fh, "|tr '[a-z]' '[A-Z]'");
open(my $fh, "|-", "tr '[a-z]' '[A-Z]'");
open(my $fh, "|-") || exec 'tr', '[a-z]', '[A-Z]';
open(my $fh, "|-", "tr", '[a-z]', '[A-Z]');
open(my $fh, "cat -n '$file'|");
open(my $fh, "-|", "cat -n '$file'");
open(my $fh, "-|") || exec "cat", "-n", $file;
open(my $fh, "-|", "cat", "-n", $file);
每個區塊中的最後兩個範例顯示管線為「清單形式」,這在所有平台上尚未受到支援。(如果您的平台有真正的 fork
,例如 Linux 和 macOS,您可以使用清單形式;它也能在 Windows 上搭配 Perl 5.22 或更新版本使用。)您會想要使用管線的清單形式,以便您可以將文字參數傳遞給指令,而不用擔心 shell 會詮釋其中的任何 shell 超字元。不過,這也會禁止您開啟包含 shell 超字元的指令的管線,例如
open(my $fh, "|cat -n | expand -4 | lpr")
|| die "Can't open pipeline to lpr: $!";
請參閱 perlipc 中的「安全管線開啟」,以取得更多範例。
您也可以在 Bourne shell 傳統中,指定以 >&
開頭的 EXPR,這種情況下,字串的其餘部分會被詮釋為要複製(如同 dup(2))並開啟的檔案處理常式(或檔案描述符,如果是數字)的名稱。您可以在 >
、>>
、<
、+>
、+>>
和 +<
之後使用 &
。您指定的模式應與原始檔案處理常式的模式相符。(複製檔案處理常式不會考量 IO 緩衝區的任何現有內容。)如果您使用三個參數的形式,則您可以傳遞數字、檔案處理常式名稱或一般的「glob 參照」。
以下是使用各種方法儲存、重新導向和還原 STDOUT
和 STDERR
的腳本
#!/usr/bin/perl
open(my $oldout, ">&STDOUT")
or die "Can't dup STDOUT: $!";
open(OLDERR, ">&", \*STDERR)
or die "Can't dup STDERR: $!";
open(STDOUT, '>', "foo.out")
or die "Can't redirect STDOUT: $!";
open(STDERR, ">&STDOUT")
or die "Can't dup STDOUT: $!";
select STDERR; $| = 1; # make unbuffered
select STDOUT; $| = 1; # make unbuffered
print STDOUT "stdout 1\n"; # this works for
print STDERR "stderr 1\n"; # subprocesses too
open(STDOUT, ">&", $oldout)
or die "Can't dup \$oldout: $!";
open(STDERR, ">&OLDERR")
or die "Can't dup OLDERR: $!";
print STDOUT "stdout 2\n";
print STDERR "stderr 2\n";
如果您指定 '<&=X'
,其中 X
是檔案描述符數字或檔案處理常式,則 Perl 會執行等同於 C 的 fdopen(3) 檔案描述符(且不會呼叫 dup(2));這會更節省檔案描述符。例如
# open for input, reusing the fileno of $fd
open(my $fh, "<&=", $fd)
或
open(my $fh, "<&=$fd")
或
# open for append, using the fileno of $oldfh
open(my $fh, ">>&=", $oldfh)
節省檔案處理常式也很有用(除了節省之外),例如當某個東西依賴於檔案描述符時,例如使用 flock
鎖定。如果您只執行 open(my $A, ">>&", $B)
,檔案處理常式 $A
的檔案描述符會與 $B
不同,因此 flock($A)
既不會 flock($B)
,反之亦然。但使用 open(my $A, ">>&=", $B)
,檔案處理常式會共用相同的底層系統檔案描述符。
請注意,在 Perl 5.8.0 之前的版本中,Perl 使用標準 C 函式庫的 fdopen(3) 來實作 =
功能。在許多 Unix 系統上,當檔案描述符超過特定值(通常為 255)時,fdopen(3) 會失敗。對於 Perl 5.8.0 及更新版本,PerlIO(通常)是預設值。
此區段說明在最佳實務之外呼叫 open
的方法;您可能會在舊程式碼中遇到這些用法。Perl 沒有明確將它們的使用視為已棄用,但為了清楚和可讀性,也不建議在新的程式碼中使用它們。
在呼叫的一和二參數形式中,模式和檔名應串接(按此順序),最好以空白分隔。在模式為 <
時,您可以在這些形式中省略模式,但不要這樣做。如果檔名參數是已知的文字,則可以使用 open
的二參數形式。
open(my $dbase, "+<dbase.mine") # ditto
or die "Can't open 'dbase.mine' for update: $!";
在二參數(和一參數)形式中,開啟 <-
或 -
會開啟 STDIN,而開啟 >-
會開啟 STDOUT。
新程式碼應優先使用 open
的三參數形式,而不是這種舊形式。將模式和檔名宣告為兩個不同的參數,可以避免兩者之間的混淆。
open
作為捷徑,一參數呼叫會從與檔案處理相同名稱的全域標量變數中取得檔名
$ARTICLE = 100;
open(ARTICLE)
or die "Can't find article $ARTICLE: $!\n";
較舊的樣式是使用裸字作為檔案處理,如下所示
open(FH, "<", "input.txt")
or die "Can't open < input.txt: $!";
然後您可以在 close FH
和 <FH>
等中使用 FH
作為檔案處理。請注意,它是一個全域變數,因此在處理 Perl 內建檔案處理(例如 STDOUT 和 STDIN)以外的檔案處理時,不建議使用此形式。事實上,當 bareword_filehandles
功能已停用時,對檔案處理使用裸字會產生錯誤。在 use v5.36.0
或更新版本的範圍內,此功能預設為停用。
當檔案處理的參考計數達到零時,檔案處理將會關閉。如果它是由使用 my
宣告的詞彙範圍變數,這通常表示封閉範圍的結束。但是,此自動關閉不會檢查錯誤,因此最好明確關閉檔案處理,特別是那些用於寫入的檔案處理
close($handle)
|| warn "close failed: $!";
Perl 會嘗試在任何可能執行 fork 的操作之前沖刷所有已開啟輸出檔案,但某些平台可能不支援此功能(請參閱 perlport)。為了安全起見,您可能需要設定 $|
(在 English 中為 $AUTOFLUSH
)或在任何開啟的處理上呼叫 IO::Handle
的 autoflush
方法。
在支援檔案上關閉執行旗標的系統上,將根據 $^F
的值設定新開啟的檔案描述符的旗標。請參閱 "$^F" in perlvar。
關閉任何管線檔案處理會導致父處理等待子處理完成,然後在 $?
和 ${^CHILD_ERROR_NATIVE}
中傳回狀態值。
如果 FILEHANDLE -- 在呼叫 open
的第一個參數 -- 是未定義的標量變數(或陣列或雜湊元素),會自動產生一個新的檔案代號,表示變數指定一個參考到新配置的匿名檔案代號。否則,如果 FILEHANDLE 是表達式,其值就是真正的檔案代號。(這被視為符號參考,因此 use strict "refs"
不應 生效。)
傳遞給 open
一個和兩個參數形式的檔案名稱會刪除前導和尾隨空白,並遵守正常的重新導向字元。這個屬性,稱為「神奇開啟」,通常可以發揮良好的效果。使用者可以指定檔案名稱為 "rsh cat file |",或者可以根據需要變更某些檔案名稱
$filename =~ s/(.*\.gz)\s*$/gzip -dc < $1|/;
open(my $fh, $filename)
or die "Can't open $filename: $!";
使用三個參數形式開啟檔案,其中包含任意奇怪的字元,
open(my $fh, "<", $file)
|| die "Can't open $file: $!";
否則,必須保護任何前導和尾隨空白
$file =~ s#^(\s)#./$1#;
open(my $fh, "< $file\0")
|| die "Can't open $file: $!";
(這可能無法在一些奇特的檔案系統上執行)。應該在 open
的神奇和三個參數形式之間仔細選擇
open(my $in, $ARGV[0]) || die "Can't open $ARGV[0]: $!";
將允許使用者指定 "rsh cat file |"
形式的參數,但無法在碰巧有尾隨空白的檔案名稱上執行,而
open(my $in, "<", $ARGV[0])
|| die "Can't open $ARGV[0]: $!";
將具有完全相反的限制。(但是,一些 shell 支援語法 perl your_program.pl <( rsh cat file )
,它產生一個可以正常開啟的檔案名稱。)
open
如果你想要一個「真正的」C open(2),則應該使用 sysopen
函式,它不涉及此類神奇(但使用與 Perl open
不同的檔案模式,對應於 C fopen(3))。這是保護檔案名稱不受解釋的另一種方式。例如
use IO::Handle;
sysopen(my $fh, $path, O_RDWR|O_CREAT|O_EXCL)
or die "Can't open $path: $!";
$fh->autoflush(1);
print $fh "stuff $$\n";
seek($fh, 0, 0);
print "File contains: ", readline($fh);
請參閱 seek
,了解有關混合讀寫的一些詳細資訊。
請參閱 perlport 中的「open」。