📄 perlopentut.pod
字号:
=head1 Open E<agrave> la CIf you want the convenience of the shell, then Perl's C<open> isdefinitely the way to go. On the other hand, if you want finer precisionthan C's simplistic fopen(3S) provides you should look to Perl'sC<sysopen>, which is a direct hook into the open(2) system call.That does mean it's a bit more involved, but that's the price of precision.C<sysopen> takes 3 (or 4) arguments. sysopen HANDLE, PATH, FLAGS, [MASK]The HANDLE argument is a filehandle just as with C<open>. The PATH isa literal path, one that doesn't pay attention to any greater-thans orless-thans or pipes or minuses, nor ignore whitespace. If it's there,it's part of the path. The FLAGS argument contains one or more valuesderived from the Fcntl module that have been or'd together using thebitwise "|" operator. The final argument, the MASK, is optional; ifpresent, it is combined with the user's current umask for the creationmode of the file. You should usually omit this.Although the traditional values of read-only, write-only, and read-writeare 0, 1, and 2 respectively, this is known not to hold true on somesystems. Instead, it's best to load in the appropriate constants firstfrom the Fcntl module, which supplies the following standard flags: O_RDONLY Read only O_WRONLY Write only O_RDWR Read and write O_CREAT Create the file if it doesn't exist O_EXCL Fail if the file already exists O_APPEND Append to the file O_TRUNC Truncate the file O_NONBLOCK Non-blocking accessLess common flags that are sometimes available on some operatingsystems include C<O_BINARY>, C<O_TEXT>, C<O_SHLOCK>, C<O_EXLOCK>,C<O_DEFER>, C<O_SYNC>, C<O_ASYNC>, C<O_DSYNC>, C<O_RSYNC>,C<O_NOCTTY>, C<O_NDELAY> and C<O_LARGEFILE>. Consult your open(2)manpage or its local equivalent for details. (Note: starting fromPerl release 5.6 the C<O_LARGEFILE> flag, if available, is automaticallyadded to the sysopen() flags because large files are the default.)Here's how to use C<sysopen> to emulate the simple C<open> calls we hadbefore. We'll omit the C<|| die $!> checks for clarity, but make sureyou always check the return values in real code. These aren't quitethe same, since C<open> will trim leading and trailing whitespace,but you'll get the idea.To open a file for reading: open(FH, "< $path"); sysopen(FH, $path, O_RDONLY);To open a file for writing, creating a new file if needed or else truncatingan old file: open(FH, "> $path"); sysopen(FH, $path, O_WRONLY | O_TRUNC | O_CREAT);To open a file for appending, creating one if necessary: open(FH, ">> $path"); sysopen(FH, $path, O_WRONLY | O_APPEND | O_CREAT);To open a file for update, where the file must already exist: open(FH, "+< $path"); sysopen(FH, $path, O_RDWR);And here are things you can do with C<sysopen> that you cannot do witha regular C<open>. As you'll see, it's just a matter of controlling theflags in the third argument.To open a file for writing, creating a new file which must not previouslyexist: sysopen(FH, $path, O_WRONLY | O_EXCL | O_CREAT);To open a file for appending, where that file must already exist: sysopen(FH, $path, O_WRONLY | O_APPEND);To open a file for update, creating a new file if necessary: sysopen(FH, $path, O_RDWR | O_CREAT);To open a file for update, where that file must not already exist: sysopen(FH, $path, O_RDWR | O_EXCL | O_CREAT);To open a file without blocking, creating one if necessary: sysopen(FH, $path, O_WRONLY | O_NONBLOCK | O_CREAT);=head2 Permissions E<agrave> la modeIf you omit the MASK argument to C<sysopen>, Perl uses the octal value0666. The normal MASK to use for executables and directories shouldbe 0777, and for anything else, 0666.Why so permissive? Well, it isn't really. The MASK will be modifiedby your process's current C<umask>. A umask is a number representingI<disabled> permissions bits; that is, bits that will not be turned onin the created files' permissions field.For example, if your C<umask> were 027, then the 020 part woulddisable the group from writing, and the 007 part would disable othersfrom reading, writing, or executing. Under these conditions, passingC<sysopen> 0666 would create a file with mode 0640, since C<0666 & ~027>is 0640.You should seldom use the MASK argument to C<sysopen()>. That takesaway the user's freedom to choose what permission new files will have.Denying choice is almost always a bad thing. One exception would be forcases where sensitive or private data is being stored, such as with mailfolders, cookie files, and internal temporary files.=head1 Obscure Open Tricks=head2 Re-Opening Files (dups)Sometimes you already have a filehandle open, and want to make anotherhandle that's a duplicate of the first one. In the shell, we place anampersand in front of a file descriptor number when doing redirections.For example, C<< 2>&1 >> makes descriptor 2 (that's STDERR in Perl)be redirected into descriptor 1 (which is usually Perl's STDOUT).The same is essentially true in Perl: a filename that begins with anampersand is treated instead as a file descriptor if a number, or as afilehandle if a string. open(SAVEOUT, ">&SAVEERR") || die "couldn't dup SAVEERR: $!"; open(MHCONTEXT, "<&4") || die "couldn't dup fd4: $!";That means that if a function is expecting a filename, but you don'twant to give it a filename because you already have the file open, youcan just pass the filehandle with a leading ampersand. It's best touse a fully qualified handle though, just in case the function happensto be in a different package: somefunction("&main::LOGFILE");This way if somefunction() is planning on opening its argument, it canjust use the already opened handle. This differs from passing a handle,because with a handle, you don't open the file. Here you have somethingyou can pass to open.If you have one of those tricky, newfangled I/O objects that the C++folks are raving about, then this doesn't work because those aren't aproper filehandle in the native Perl sense. You'll have to use fileno()to pull out the proper descriptor number, assuming you can: use IO::Socket; $handle = IO::Socket::INET->new("www.perl.com:80"); $fd = $handle->fileno; somefunction("&$fd"); # not an indirect function callIt can be easier (and certainly will be faster) just to use realfilehandles though: use IO::Socket; local *REMOTE = IO::Socket::INET->new("www.perl.com:80"); die "can't connect" unless defined(fileno(REMOTE)); somefunction("&main::REMOTE");If the filehandle or descriptor number is preceded not just with a simple"&" but rather with a "&=" combination, then Perl will not create acompletely new descriptor opened to the same place using the dup(2)system call. Instead, it will just make something of an alias to theexisting one using the fdopen(3S) library call. This is slightly moreparsimonious of systems resources, although this is less a concernthese days. Here's an example of that: $fd = $ENV{"MHCONTEXTFD"}; open(MHCONTEXT, "<&=$fd") or die "couldn't fdopen $fd: $!";If you're using magic C<< <ARGV> >>, you could even pass in as acommand line argument in @ARGV something like C<"<&=$MHCONTEXTFD">,but we've never seen anyone actually do this.=head2 Dispelling the DweomerPerl is more of a DWIMmer language than something like Java--where DWIMis an acronym for "do what I mean". But this principle sometimes leadsto more hidden magic than one knows what to do with. In this way, Perlis also filled with I<dweomer>, an obscure word meaning an enchantment.Sometimes, Perl's DWIMmer is just too much like dweomer for comfort.If magic C<open> is a bit too magical for you, you don't have to turnto C<sysopen>. To open a file with arbitrary weird characters init, it's necessary to protect any leading and trailing whitespace.Leading whitespace is protected by inserting a C<"./"> in front of afilename that starts with whitespace. Trailing whitespace is protectedby appending an ASCII NUL byte (C<"\0">) at the end of the string. $file =~ s#^(\s)#./$1#; open(FH, "< $file\0") || die "can't open $file: $!";This assumes, of course, that your system considers dot the currentworking directory, slash the directory separator, and disallows ASCIINULs within a valid filename. Most systems follow these conventions,including all POSIX systems as well as proprietary Microsoft systems.The only vaguely popular system that doesn't work this way is the"Classic" Macintosh system, which uses a colon where the rest of ususe a slash. Maybe C<sysopen> isn't such a bad idea after all.If you want to use C<< <ARGV> >> processing in a totally boringand non-magical way, you could do this first: # "Sam sat on the ground and put his head in his hands. # 'I wish I had never come here, and I don't want to see # no more magic,' he said, and fell silent." for (@ARGV) { s#^([^./])#./$1#; $_ .= "\0"; } while (<>) { # now process $_ } But be warned that users will not appreciate being unable to use "-"to mean standard input, per the standard convention.=head2 Paths as OpensYou've probably noticed how Perl's C<warn> and C<die> functions canproduce messages like: Some warning at scriptname line 29, <FH> line 7.That's because you opened a filehandle FH, and had read in seven recordsfrom it. But what was the name of the file, rather than the handle?If you aren't running with C<strict refs>, or if you've turned them offtemporarily, then all you have to do is this: open($path, "< $path") || die "can't open $path: $!"; while (<$path>) { # whatever } Since you're using the pathname of the file as its handle,you'll get warnings more like Some warning at scriptname line 29, </etc/motd> line 7.=head2 Single Argument OpenRemember how we said that Perl's open took two arguments? That was apassive prevarication. You see, it can also take just one argument.If and only if the variable is a global variable, not a lexical, youcan pass C<open> just one argument, the filehandle, and it will get the path from the global scalar variable of the same name. $FILE = "/etc/motd"; open FILE or die "can't open $FILE: $!"; while (<FILE>) { # whatever } Why is this here? Someone has to cater to the hysterical porpoises.It's something that's been in Perl since the very beginning, if notbefore.=head2 Playing with STDIN and STDOUTOne clever move with STDOUT is to explicitly close it when you're donewith the program. END { close(STDOUT) || die "can't close stdout: $!" }If you don't do this, and your program fills up the disk partition dueto a command line redirection, it won't report the error exit with afailure status.You don't have to accept the STDIN and STDOUT you were given. You arewelcome to reopen them if you'd like. open(STDIN, "< datafile") || die "can't open datafile: $!"; open(STDOUT, "> output") || die "can't open output: $!";And then these can be accessed directly or passed on to subprocesses.This makes it look as though the program were initially invokedwith those redirections from the command line.It's probably more interesting to connect these to pipes. For example: $pager = $ENV{PAGER} || "(less || more)"; open(STDOUT, "| $pager") || die "can't fork a pager: $!";This makes it appear as though your program were called with its stdoutalready piped into your pager. You can also use this kind of thingin conjunction with an implicit fork to yourself. You might do thisif you would rather handle the post processing in your own program,just in a different process: head(100); while (<>) { print; } sub head { my $lines = shift || 20; return if $pid = open(STDOUT, "|-"); # return if parent die "cannot fork: $!" unless defined $pid; while (<STDIN>) { last if --$lines < 0; print; } exit; } This technique can be applied to repeatedly push as many filters on youroutput stream as you wish.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -