⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 perlfaq5.pod

📁 ARM上的如果你对底层感兴趣
💻 POD
📖 第 1 页 / 共 3 页
字号:
        print $fh "Sending to indirect filehandle\n";
    }

Or it can localize a typeglob and use the filehandle directly:

    sub accept_fh {
        local *FH = shift;
        print  FH "Sending to localized filehandle\n";
    }

Both styles work with either objects or typeglobs of real filehandles.
(They might also work with strings under some circumstances, but this
is risky.)

    accept_fh(*STDOUT);
    accept_fh($handle);

In the examples above, we assigned the filehandle to a scalar variable
before using it.  That is because only simple scalar variables,
not expressions or subscripts into hashes or arrays, can be used with
built-ins like C<print>, C<printf>, or the diamond operator.  These are
illegal and won't even compile:

    @fd = (*STDIN, *STDOUT, *STDERR);
    print $fd[1] "Type it: ";                           # WRONG
    $got = <$fd[0]>                                     # WRONG
    print $fd[2] "What was that: $got";                 # WRONG

With C<print> and C<printf>, you get around this by using a block and
an expression where you would place the filehandle:

    print  { $fd[1] } "funny stuff\n";
    printf { $fd[1] } "Pity the poor %x.\n", 3_735_928_559;
    # Pity the poor deadbeef.

That block is a proper block like any other, so you can put more
complicated code there.  This sends the message out to one of two places:

    $ok = -x "/bin/cat";                
    print { $ok ? $fd[1] : $fd[2] } "cat stat $ok\n";
    print { $fd[ 1+ ($ok || 0) ]  } "cat stat $ok\n";           

This approach of treating C<print> and C<printf> like object methods
calls doesn't work for the diamond operator.  That's because it's a
real operator, not just a function with a comma-less argument.  Assuming
you've been storing typeglobs in your structure as we did above, you
can use the built-in function named C<readline> to reads a record just
as C<E<lt>E<gt>> does.  Given the initialization shown above for @fd, this
would work, but only because readline() require a typeglob.  It doesn't
work with objects or strings, which might be a bug we haven't fixed yet.

    $got = readline($fd[0]);

Let it be noted that the flakiness of indirect filehandles is not
related to whether they're strings, typeglobs, objects, or anything else.
It's the syntax of the fundamental operators.  Playing the object
game doesn't help you at all here.

=head2 How can I set up a footer format to be used with write()?

There's no builtin way to do this, but L<perlform> has a couple of
techniques to make it possible for the intrepid hacker.

=head2 How can I write() into a string?

See L<perlform> for an swrite() function.

=head2 How can I output my numbers with commas added?

This one will do it for you:

    sub commify {
	local $_  = shift;
	1 while s/^(-?\d+)(\d{3})/$1,$2/;
	return $_;
    }

    $n = 23659019423.2331;
    print "GOT: ", commify($n), "\n";

    GOT: 23,659,019,423.2331

You can't just:

    s/^(-?\d+)(\d{3})/$1,$2/g;

because you have to put the comma in and then recalculate your
position.

Alternatively, this commifies all numbers in a line regardless of
whether they have decimal portions, are preceded by + or -, or
whatever:

    # from Andrew Johnson <ajohnson@gpu.srv.ualberta.ca>
    sub commify {
       my $input = shift;
        $input = reverse $input;
        $input =~ s<(\d\d\d)(?=\d)(?!\d*\.)><$1,>g;
        return reverse $input;
    }

=head2 How can I translate tildes (~) in a filename?

Use the E<lt>E<gt> (glob()) operator, documented in L<perlfunc>.  This
requires that you have a shell installed that groks tildes, meaning
csh or tcsh or (some versions of) ksh, and thus may have portability
problems.  The Glob::KGlob module (available from CPAN) gives more
portable glob functionality.

Within Perl, you may use this directly:

	$filename =~ s{
	  ^ ~             # find a leading tilde
	  (               # save this in $1
	      [^/]        # a non-slash character
	            *     # repeated 0 or more times (0 means me)
	  )
	}{
	  $1
	      ? (getpwnam($1))[7]
	      : ( $ENV{HOME} || $ENV{LOGDIR} )
	}ex;

=head2 How come when I open a file read-write it wipes it out?

Because you're using something like this, which truncates the file and
I<then> gives you read-write access:

    open(FH, "+> /path/name");		# WRONG (almost always)

Whoops.  You should instead use this, which will fail if the file
doesn't exist.  Using "E<gt>" always clobbers or creates. 
Using "E<lt>" never does either.  The "+" doesn't change this.

Here are examples of many kinds of file opens.  Those using sysopen()
all assume

    use Fcntl;

To open file for reading:

    open(FH, "< $path")                                 || die $!;
    sysopen(FH, $path, O_RDONLY)                        || die $!;

To open file for writing, create new file if needed or else truncate old file:

    open(FH, "> $path") || die $!;
    sysopen(FH, $path, O_WRONLY|O_TRUNC|O_CREAT)        || die $!;
    sysopen(FH, $path, O_WRONLY|O_TRUNC|O_CREAT, 0666)  || die $!;

To open file for writing, create new file, file must not exist:

    sysopen(FH, $path, O_WRONLY|O_EXCL|O_CREAT)         || die $!;
    sysopen(FH, $path, O_WRONLY|O_EXCL|O_CREAT, 0666)   || die $!;

To open file for appending, create if necessary:

    open(FH, ">> $path") || die $!;
    sysopen(FH, $path, O_WRONLY|O_APPEND|O_CREAT)       || die $!;
    sysopen(FH, $path, O_WRONLY|O_APPEND|O_CREAT, 0666) || die $!;

To open file for appending, file must exist:

    sysopen(FH, $path, O_WRONLY|O_APPEND)               || die $!;

To open file for update, file must exist:

    open(FH, "+< $path")                                || die $!;
    sysopen(FH, $path, O_RDWR)                          || die $!;

To open file for update, create file if necessary:

    sysopen(FH, $path, O_RDWR|O_CREAT)                  || die $!;
    sysopen(FH, $path, O_RDWR|O_CREAT, 0666)            || die $!;

To open file for update, file must not exist:

    sysopen(FH, $path, O_RDWR|O_EXCL|O_CREAT)           || die $!;
    sysopen(FH, $path, O_RDWR|O_EXCL|O_CREAT, 0666)     || die $!;

To open a file without blocking, creating if necessary:

    sysopen(FH, "/tmp/somefile", O_WRONLY|O_NDELAY|O_CREAT)
	    or die "can't open /tmp/somefile: $!":

Be warned that neither creation nor deletion of files is guaranteed to
be an atomic operation over NFS.  That is, two processes might both
successful create or unlink the same file!  Therefore O_EXCL
isn't so exclusive as you might wish.

=head2 Why do I sometimes get an "Argument list too long" when I use <*>?

The C<E<lt>E<gt>> operator performs a globbing operation (see above).
By default glob() forks csh(1) to do the actual glob expansion, but
csh can't handle more than 127 items and so gives the error message
C<Argument list too long>.  People who installed tcsh as csh won't
have this problem, but their users may be surprised by it.

To get around this, either do the glob yourself with C<Dirhandle>s and
patterns, or use a module like Glob::KGlob, one that doesn't use the
shell to do globbing.

=head2 Is there a leak/bug in glob()?

Due to the current implementation on some operating systems, when you
use the glob() function or its angle-bracket alias in a scalar
context, you may cause a leak and/or unpredictable behavior.  It's
best therefore to use glob() only in list context.

=head2 How can I open a file with a leading "E<gt>" or trailing blanks?

Normally perl ignores trailing blanks in filenames, and interprets
certain leading characters (or a trailing "|") to mean something
special.  To avoid this, you might want to use a routine like this.
It makes incomplete pathnames into explicit relative ones, and tacks a
trailing null byte on the name to make perl leave it alone:

    sub safe_filename {
	local $_  = shift;
	return m#^/#
		? "$_\0"
		: "./$_\0";
    }

    $fn = safe_filename("<<<something really wicked   ");
    open(FH, "> $fn") or "couldn't open $fn: $!";

You could also use the sysopen() function (see L<perlfunc/sysopen>).

=head2 How can I reliably rename a file?

Well, usually you just use Perl's rename() function.  But that may
not work everywhere, in particular, renaming files across file systems.
If your operating system supports a mv(1) program or its moral equivalent,
this works:

    rename($old, $new) or system("mv", $old, $new);

It may be more compelling to use the File::Copy module instead.  You
just copy to the new file to the new name (checking return values),
then delete the old one.  This isn't really the same semantics as a
real rename(), though, which preserves metainformation like
permissions, timestamps, inode info, etc.

The newer version of File::Copy export a move() function.

=head2 How can I lock a file?

Perl's builtin flock() function (see L<perlfunc> for details) will call
flock(2) if that exists, fcntl(2) if it doesn't (on perl version 5.004 and
later), and lockf(3) if neither of the two previous system calls exists.
On some systems, it may even use a different form of native locking.
Here are some gotchas with Perl's flock():

=over 4

=item 1

Produces a fatal error if none of the three system calls (or their
close equivalent) exists.

=item 2

lockf(3) does not provide shared locking, and requires that the
filehandle be open for writing (or appending, or read/writing).

=item 3

Some versions of flock() can't lock files over a network (e.g. on NFS
file systems), so you'd need to force the use of fcntl(2) when you
build Perl.  See the flock entry of L<perlfunc>, and the F<INSTALL>
file in the source distribution for information on building Perl to do
this.

=back

=head2 What can't I just open(FH, ">file.lock")?

A common bit of code B<NOT TO USE> is this:

    sleep(3) while -e "file.lock";	# PLEASE DO NOT USE
    open(LCK, "> file.lock");		# THIS BROKEN CODE

This is a classic race condition: you take two steps to do something
which must be done in one.  That's why computer hardware provides an
atomic test-and-set instruction.   In theory, this "ought" to work:

    sysopen(FH, "file.lock", O_WRONLY|O_EXCL|O_CREAT)
		or die "can't open  file.lock: $!":

except that lamentably, file creation (and deletion) is not atomic
over NFS, so this won't work (at least, not every time) over the net.
Various schemes involving involving link() have been suggested, but
these tend to involve busy-wait, which is also subdesirable.

=head2 I still don't get locking.  I just want to increment the number in the file.  How can I do this?

Didn't anyone ever tell you web-page hit counters were useless?
They don't count number of hits, they're a waste of time, and they serve
only to stroke the writer's vanity.  Better to pick a random number.
It's more realistic.

Anyway, this is what you can do if you can't help yourself.

    use Fcntl;
    sysopen(FH, "numfile", O_RDWR|O_CREAT) 	 or die "can't open numfile: $!";
    flock(FH, 2) 				 or die "can't flock numfile: $!";
    $num = <FH> || 0;
    seek(FH, 0, 0) 				 or die "can't rewind numfile: $!";
    truncate(FH, 0) 				 or die "can't truncate numfile: $!";
    (print FH $num+1, "\n")			 or die "can't write numfile: $!";
    # DO NOT UNLOCK THIS UNTIL YOU CLOSE
    close FH 					 or die "can't close numfile: $!";

Here's a much better web-page hit counter:

    $hits = int( (time() - 850_000_000) / rand(1_000) );

If the count doesn't impress your friends, then the code might.  :-)

=head2 How do I randomly update a binary file?

If you're just trying to patch a binary, in many cases something as
simple as this works:

    perl -i -pe 's{window manager}{window mangler}g' /usr/bin/emacs

However, if you have fixed sized records, then you might do something more
like this:

    $RECSIZE = 220; # size of record, in bytes
    $recno   = 37;  # which record to update
    open(FH, "+<somewhere") || die "can't update somewhere: $!";
    seek(FH, $recno * $RECSIZE, 0);
    read(FH, $record, $RECSIZE) == $RECSIZE || die "can't read record $recno: $!";
    # munge the record
    seek(FH, $recno * $RECSIZE, 0);
    print FH $record;
    close FH;

Locking and error checking are left as an exercise for the reader.
Don't forget them, or you'll be quite sorry.

=head2 How do I get a file's timestamp in perl?

If you want to retrieve the time at which the file was last read,
written, or had its meta-data (owner, etc) changed, you use the B<-M>,
B<-A>, or B<-C> filetest operations as documented in L<perlfunc>.  These
retrieve the age of the file (measured against the start-time of your
program) in days as a floating point number.  To retrieve the "raw"
time in seconds since the epoch, you would call the stat function,
then use localtime(), gmtime(), or POSIX::strftime() to convert this
into human-readable form.

Here's an example:

    $write_secs = (stat($file))[9];
    printf "file %s updated at %s\n", $file,
	scalar localtime($write_secs);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -