tlhackque has asked for the wisdom of the Perl Monks concerning the following question:

Consider a typical magic diamond (ARGV) usage, reducto ad absurdum:
perl -e'while(<>){1;}continue{print "$ARGV\n" if eof;}; print $!' fi +le1 file2 file3 file1 Can't open file2: No such file or directory at -e line 1, <> line 222. file3 Bad file descriptor

Note that file 2 doesn't exist, and Perl generates an error message. So the user knows, probably.

However, after the loop, there doesn't seem to be a way for the program to detect that a file wasn't processed. In real life, that could be awkward. Am I missing something?

I suppose that in a continue block, I could push $ARGV onto an array using eof and compare the result to @ARGV after the loop. But that seems awkward and ugly.

Saner alternatives (other than forgoing magic)?

This communication may not represent my employer's views, if any, on the matters discussed.

Replies are listed 'Best First'.
Re: Diamond errors
by choroba (Cardinal) on Jan 21, 2023 at 16:52 UTC
    That's how sed behaves, too, and many other tools.

    If you want to handle missing files, don't use the diamond, use open or something like

    BEGIN { -f or die "No file $_" for @ARGV }

    BTW, $! only contains a meaningful value right after something went wrong. If you print it after processing an existing file, its value might be totally irrelevant (I'm getting Inappropriate ioctl for device).

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Diamond errors
by hippo (Archbishop) on Jan 21, 2023 at 17:48 UTC
    However, after the loop, there doesn't seem to be a way for the program to detect that a file wasn't processed. In real life, that could be awkward.

    Simple solution: use strictures.

    perl -Mstrictures -e'while(<>){1;}continue{print "$ARGV\n" if eof;}; p +rint $!' file1 file2 file3 file1 Can't open file2: No such file or directory at -e line 1, <> line 1.

    That should make it pretty obvious even to the most obtuse of users that something has gone badly wrong.

    If you are after a less abrupt, more hand-holding approach then yes, I think it probably best to roll your own.


    🦛

Re: Diamond errors
by haukex (Archbishop) on Jan 21, 2023 at 17:40 UTC

    Perhaps fatal warnings?

    $ perl -ne 'print$ARGV,": ",$_' file1 file2 file3 file1: Hello Can't open file2: No such file or directory at -e line 1, <> line 1. file3: World $ perl -Mwarnings=FATAL,inplace -ne 'print$ARGV,": ",$_' file1 file2 f +ile3 file1: Hello Can't open file2: No such file or directory at -e line 1, <> line 1.
Re: Diamond errors
by LanX (Saint) on Jan 21, 2023 at 18:42 UTC
    > However, after the loop, there doesn't seem to be a way for the program to detect that a file wasn't processed

    If you want the errors to be kept till after the loop, maybe try a $SIG{"__WARN__"} handler

    d:\temp>perl -e"$SIG{"__WARN__"} = sub {push @err,$_[0]; print STDERR +$_[0]}; while(<>) {1};print qq<---\n @err>" file1 file2 file3 Can't open file1: No such file or directory at -e line 1. Can't open file2: No such file or directory at -e line 1. Can't open file3: No such file or directory at -e line 1. --- Can't open file1: No such file or directory at -e line 1. Can't open file2: No such file or directory at -e line 1. Can't open file3: No such file or directory at -e line 1. d:\temp>

    This is perl 5, version 32, subversion 1 (v5.32.1) built for MSWin32-x64-multi-thread

    you can also only record the filename by pushing $ARGV instead and control what the user sees.

    furthermore you can scope the handler with local tightly around the relevant code

    { local $SIG{"__WARN__"}= sub {...}; while(<>){...} }

    Cheers Rolf
    (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
    Wikisyntax for the Monastery

Re: Diamond errors
by jwkrahn (Abbot) on Jan 21, 2023 at 21:52 UTC
    I could push $ARGV onto an array using eof and compare the result to @ARGV after the loop.

    You can't do that because perl removes $ARGV from @ARGV for each file it processes so after the loop @ARGV is empty.

    Naked blocks are fun! -- Randal L. Schwartz, Perl hacker
Re: Diamond errors
by LanX (Saint) on Jan 21, 2023 at 22:18 UTC
    > perl -e'while(<>){1;}continue{print "$ARGV\n" if eof;}; print $!' file1 file2 file3

    > I could push $ARGV onto an array using eof and compare the result to @ARGV after the loop

    please note that the continue will not be executed for empty files, although they exist.

    LanX@MDXN MINGW64 / $ ls -l file* -rw-r--r-- 1 LanX 1049089 0 Jan 21 23:12 file1 -rw-r--r-- 1 LanX 1049089 2 Jan 21 23:13 file3 LanX@MDXN MINGW64 / $ perl -e'while(<>){1;}continue{print "---$ARGV\n" if eof;}; print $!' + file1 file2 file3 Can't open file2: No such file or directory at -e line 1. ---file3

    Cheers Rolf
    (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
    Wikisyntax for the Monastery

Re: Diamond errors
by jwkrahn (Abbot) on Jan 21, 2023 at 21:59 UTC

    Use a file glob:

    $ touch file1 file3 $ perl -e'while(<>){1;}continue{print "$ARGV\n" if eof;}; print $!' fi +le1 file2 file3 Can't open file2: No such file or directory at -e line 1. $ perl -e'while(<>){1;}continue{print "$ARGV\n" if eof;}; print $!' fi +le[123] $
    Naked blocks are fun! -- Randal L. Schwartz, Perl hacker
Re: Diamond errors
by LanX (Saint) on Jan 21, 2023 at 16:53 UTC
    I'm not sure I understand the question ...

    Is it directed to one-liners only or is it just a demo?

    you can always check with -e if the file exists and generate a meaningful message

    checking $! "after the loop" seems wrong, because they are reset.

    Maybe push them all onto your own @errors_stack which you print afterwards?

    edit

    > there doesn't seem to be a way for the program to detect that a file wasn't processed

    IIRC: $. should tell you the current line number.

    You could check if it's false at eof

    Cheers Rolf
    (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
    Wikisyntax for the Monastery