open 也可能指代模块:open
open FILEHANDLE,MODE,EXPR
open FILEHANDLE,MODE,EXPR,LIST
open FILEHANDLE,MODE,REFERENCE
open FILEHANDLE,EXPR
open FILEHANDLE

将一个内部 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)rr+ww+aa+ 模式。

不同模式在实际应用中的更多示例

# 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 操作涉及管道,则返回值恰好是子进程的进程 ID。

在打开文件时,如果请求失败,继续执行通常不是一个好主意,因此 open 通常与 die 一起使用。即使您希望代码在打开文件失败时执行其他操作,而不是 die,您也应该始终检查打开文件的返回值。

在 MODE 中指定 I/O 层

您可以使用 open 函数的三参数形式来指定要应用于新文件句柄的 I/O 层(有时称为“规则”)。这些会影响输入和输出的处理方式(有关详细信息,请参阅 openPerlIO)。例如

# 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

要将 STDOUTSTDERR(重新)打开为内存文件,请先关闭它

    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;否则没有。

有关 PerlIO 的详细信息,请参阅 perliol

将文件句柄打开到命令

如果 MODE 为 |-,则文件名被解释为一个命令,输出将被管道传输到该命令,如果 MODE 为 -|,则文件名被解释为一个命令,该命令将输出管道传输到我们。在两个参数(和一个参数)形式中,应该用命令替换破折号(-)。有关此的更多示例,请参阅 "在 perlipc 中使用 open() 进行 IPC"。(您不允许 open 到一个同时管道传输输入和输出的命令,但请参阅 IPC::Open2IPC::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: $!";

在采用三个或更多参数的管道打开形式中,如果指定了 LIST(命令名称后的额外参数),则如果平台支持,LIST 将成为调用命令的参数。对于非管道模式,具有三个以上参数的 open 的含义尚未定义,但实验性的“层”可能会赋予额外的 LIST 参数意义。

如果您在命令中打开一个管道 -(即,使用 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 管道进出。在子进程中,文件句柄未打开——I/O 从新的 STDOUT/STDIN 进出。通常,当您想要对管道命令的执行方式进行更多控制时,例如在运行 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,则可以使用列表形式;它在使用 Perl 5.22 或更高版本的 Windows 上也能正常工作。)您可能希望使用管道的列表形式,这样您就可以将文字参数传递给命令,而无需担心 shell 解释其中的任何 shell 元字符。但是,这也阻止您将管道打开到包含 shell 元字符的命令,例如

open(my $fh, "|cat -n | expand -4 | lpr")
	|| die "Can't open pipeline to lpr: $!";

有关更多示例,请参阅 perlipc 中的“安全管道打开”

复制文件句柄

您也可以按照 Bourne shell 的传统,指定以 >& 开头的 EXPR,在这种情况下,字符串的其余部分将被解释为要复制(如 dup(2))并打开的文件句柄(或文件描述符,如果为数字)。您可以在 >>><+>+>>+< 后使用 &。您指定的模式应与原始文件句柄的模式匹配。(复制文件句柄不会考虑 I/O 缓冲区中任何现有的内容。)如果您使用三参数形式,则可以传递数字、文件句柄名称或正常的“对 glob 的引用”。

以下是一个使用各种方法保存、重定向和恢复 STDOUTSTDERR 的脚本

#!/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)`,文件句柄将共享相同的基础系统文件描述符。

请注意,在低于 5.8.0 的 Perl 版本中,Perl 使用标准 C 库的 fdopen(3) 来实现 `=` 功能。在许多 Unix 系统上,当文件描述符超过某个值(通常为 255)时,fdopen(3) 会失败。对于 5.8.0 及更高版本的 Perl,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";

这里 `$ARTICLE` 必须是一个全局(包)标量变量,而不是使用 mystate 声明的变量。

将文件句柄分配给一个裸字

一种旧的风格是使用一个裸字作为文件句柄,例如

open(FH, "<", "input.txt")
   or die "Can't open < input.txt: $!";

然后你就可以使用 `FH` 作为文件句柄,在 `close 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` 方法来处理任何打开的句柄。

在支持文件上的 close-on-exec 标志的系统上,该标志将根据 $^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 ),它会生成一个可以正常打开的文件名。)

调用 C 样式的 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

可移植性问题

参见 "open" 在 perlport 中