Score:0

perl - get non-zero exit code if at least one of the listed files does not exist

it flag

As for the cat command, which returns an exit code other than 0 if at least one file in the list does not exist, but continuing its execution, I would also like the same behavior for the "perl" command, which while continuing its execution, unfortunately it does not return a non-0 exit code when encountering one or more missing files.

This solution provided by another topic https://stackoverflow.com/questions/22192300:

perl -p -i -e 'BEGIN{ -f $ARGV[0] or die"no file" } s/foo/bar/' non-existent-file.txt

Correctly returns a non-0 exit code when a file is not found but does not continue executing for other files. Indeed this:

perl -p -i -e 'BEGIN{ -f $ARGV[0] or die"no file" } s/foo/bar/' non-existent-file.txt existent-file.txt

doesn't give the output of the second file but only the error for the first non-existent file.

How can I solve the problem?

cn flag
Duplicate of https://stackoverflow.com/q/76209021/7552
Raffa avatar
jp flag
You are evaluating only the first argument and killing your script immediately after if the file isn't existent ... This will neither evaluate all files nor continue if the first argument is a nonexistent file.
Score:1
jp flag

I'm not a Perl guru either, but you are evaluating only the first argument and killing your script immediately after the file isn't existent ... This will neither evaluate all files passed as arguments nor continue to process other files if the first argument is a nonexistent file.

Therefor, your script is not going to work as you expect when you pass multiple arguments(filenames) to it.

You need to first evaluate all the command-line arguments with e.g. foreach my $arg (@ARGV) { ... } then set the exit code accordingly and exit with that code at the end.

i.e. something like this:

perl -p -e 'my $exit_code; BEGIN{
foreach my $arg (@ARGV) {-f $arg or $exit_code = 2} }
END{exit($exit_code)}
s/foo/bar/' non-existent-file.txt existent-file.txt

Notice(might save your time): AFAIK die doesn’t have an exit status of its own nor does it "directly" alter the script's exit status … Therefor, the exit code number 2 you see is actually for -f $ARGV[0] and it means no such file or directory … Those code numbers are limited in variety and are distinct … Therefore, it is perfectly fine to assign/set them manually in a controlled environment as such although if an argument passed might be a directory, then it might result in a different error number but that’s what "controlled" is about as you can change -f to -e for instance or double evaluate with a conditional or even add your own extended error evaluation/containment code to do exactly what you want.

Furthermore, die can have a side effect(exception) that will result in additional error codes e.g. 22(invalid argument) thus contaminating your script and that might not be what you want if you aim to preserve some actual exit status ... See why here ... You might however be able to contain/neutralize this side effect partially(although might not be reliable ... Use exit instead) with something like eval{die "blabla" ... } and in which case this might directly alter your script's exit status as well.

However, you might be able to both use die and not exit and preserve the actual exit status from -f $ARGV... at the same time while not killing your script immaturely if you properly move the evaluation to an END block with something like this:

perl -p -e 'my @arr; BEGIN{@arr = @ARGV}
END{ foreach my $arg (@arr) { -f $arg or die"no file"} }
s/foo/bar/' non-existent-file.txt existent-file.txt
Score:0
dj flag
perl -p -i -e 'BEGIN { -f $ARGV[0] or push @nofile, $ARGV[0]; } END { die "no file: ". join ", ", @nofile if @nofile; } s/foo/bar/' non-existent-file.txt existent-file.txt
it flag
Why exit code is 22 and not 2 as in the question case?
it flag
can you explain this solution please?
Packy avatar
dj flag
First, this isn't "the perl command". You're writing a one-line perl *script* in the language Perl. What the BEGIN block does is execute code before the rest of script executes. The END block executes code after the rest of the script executes. If you're married to a particular exit code, you shouldn't be using `die`, which takes a string argument, you should be using `exit` which takes a numeric argument. What my addition to your script does is delay the exit of the script with a non-zero exit code until after it has processed the existing files.
Packy avatar
dj flag
Look at Raffa's answer below. It's got the "just give me something that works" that you want.
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.