Catching exception of a shell command in Perl 6
Asked Answered
L

1

11

I had to run a shell program exiting with an error from Perl 6, so I've decided to test how it works. I've made a bash script producing an error to run it from a Perl 6 program:

$ cat prog.sh 
echo "error" >&2
exit 1

Here is how I call it from Perl 6:

put "start";
  try {
    shell "./prog.sh";
  }
put "end";

The output shows that the program exited after running the shell command.

start
error
The spawned command './prog.sh' exited unsuccessfully (exit code: 1)
  in block <unit> at b.p6 line 2

If I add a CATCH block

put "start";
  try {
    shell "./prog.sh";
    CATCH { default {} }
  }
put "end";

everything is OK, and the program works to the last line:

start
error
end

So my question: why is it necessary to add the CATCH block, while try by itself cannot tackle the error?

Lapboard answered 1/10, 2018 at 19:53 Comment(0)
Z
9

shell doesn't throw the Exception until the sink.

The try block with only the shell in it fully executes without an exception being thrown, returning the last value in the block, which then gets sunk outside the context of the try, which then throws the Exception.

You can see this with:

put "start";
  try {
    shell "./prog.sh";
    'something';
  }
put "end";

Now the shell gets sunk inside the try, which gets caught by the implicit CATCH of the try. The try block returns the last value in the block, the 'something', which then gets safely sunk outside the try.

You can also force the sink to happen inside the try:

put "start";
try {
   sink shell "./prog.sh"
}
put "end";

Your added CATCH block is just preventing the try block from returning the return value from the shell.

You can re-arrange them and see that this still blows up:

put "start";
try {
    CATCH { default {} }
    shell "./prog.sh";
}
put "end";

The best, most clear way to handle this IMHO would be to check the return from the shell yourself rather than letting it sink and throw the Exception:

put "start";
if shell "./prog.sh" {
    say 'ok'
}
else {
    say 'failed'
}
put "end";
Zoosporangium answered 2/10, 2018 at 0:46 Comment(4)
@CurtTilems Thank you very much for explaining this mystery! The only question is — why does the shell command throw an Exception only after sink? Is it a property of the shell itself or the way Perl 6 handles it? The docs on sink are very short, so I don't fully understand the concept.Lapboard
shell returns a Failure, which is a wrapper around an Exception. Only when the Failure is actually used, or sunk, will it throw the Exception it wrapped. You can call .defined or .Bool on a Failure without it throwing: the .Bool happens if you use it in an if, which is what Curt showed in the last code example.Squint
@ElizabethMattijsen Thanks! But what's the reason why it doesn't throw the Exception? To make it possible to use it in if block?Lapboard
Yes. and the .defined is for when you want to use it in a with block.Squint

© 2022 - 2024 — McMap. All rights reserved.