Throw exception on nonzero exit code
Related problem
One of the most powerful features of the Elvish shell is that it throws an exception when a program exits with non-zero.
For those few programs that returns nonzero even in normal operation (e.g. diff), catching exceptions is easier than doing manual error checking every time for every line of code.
Exceptions could be utilized in other parts of the language too (throw command for usage by the programmer, type errors, etc.).
Describe the solution you'd like
Copy the exception feature of the Elvish shell.
Describe alternatives you've considered
No response
Additional context and details
No response
The problem with that is that non-zero exit codes from externals are not always errors.
If you want to read up on how this difficulty due to the messy reality of exit codes pops up (during nushell's development), you can check out JT's blog post on that https://www.jntrnr.com/exit-codes/
@fdncred Please read previous comments before commenting. And please take a look at how Elvish does this before contributing to the discussion. Your comment didn't help anything.
The problem with that is that non-zero exit codes from externals are not always errors.
Elvish can handle that too. I am using Elvish as a daily driver, and this functionality is one of the most useful. This is what keeps me from switching to Nushell.
https://elv.sh/ref/language.html#exception-capture https://elv.sh/ref/language.html#try Also, exceptions are mentioned in other parts of the documentation, search for "exception" in that page.
As far as I understand, this is supposed to work, see #3858
Any news on this? #3858 was closed without any advancement.
Mhh we seem to properly suppress the non-zero exit code with try. But we do not return some easily digestable error, that can be processed in catch.
And ; sequences react to the non-zero exit.
/home/stefan/nushell〉try { diff }; echo test
diff: missing operand after 'diff'
diff: Try 'diff --help' for more information.
test
/home/stefan/nushell〉diff; echo test
diff: missing operand after 'diff'
diff: Try 'diff --help' for more information.
Great! This makes sense given these use cases:
- I use an external command that happens to write to stderr. I want to see that stream as it appears.
- I use an external command that can fail. I want my script to behave sanely and abort unless I use
try.
For the following use case, we have do -c!
- I run an external command that only writes to stderr when it fails. I want to propagate that as an error message in case of failure
〉do -c { diff }
Error: nu::shell::external_command (link)
× External command failed
╭─[entry #11:1:1]
1 │ do -c { diff }
· ──┬─
· ╰── External command failed
╰────
help: diff: fehlender Operand nach »diff«
diff: »diff --help« gibt Ihnen mehr Informationen.
And for this last use case, we have do -s { … } | complete:
- I run an external command where “exit status ≠ 0” doesn’t mean failure and I want to handle that:
〉do -s { LANG=C diff /etc/profile.d/jre.sh /etc/profile.d/jre.csh } | complete
╭───────────┬──────────────────────────────────────────────────╮
│ stdout │ 3,4c3 │
│ │ < append_path '/usr/lib/jvm/default/bin' │
│ │ < export PATH │
│ │ --- │
│ │ > setenv PATH "${PATH}:/usr/lib/jvm/default/bin" │
│ │ │
│ stderr │ │
│ exit_code │ 1 │
╰───────────┴──────────────────────────────────────────────────╯
I’m not sure why do -i { … } | complete swallows the exit code, but this should be good. We just need to have more extensive documentation for 1. all these use cases and 2. the different kinds of errors do can handle, but everything should be good now!
I think it would be nice (though I don't know how the implementation would go) if do -i and do -c would preserve the exit code in some way, or if there was a separate flag that did that. My use case is that I want the numeric exit code from an external command, but don't want to use complete as it prevents streaming the stdout.
You can use $env.LAST_EXIT_CODE for that I think: https://www.nushell.sh/book/stdout_stderr_exit_codes.html#exit-code
The example given in the book doesn't work in scripts for me - if the code is non-zero, it counts as an error and quits the script:
> def nonzero [] {
print "start"
do {bash -c false}
print $env.LAST_EXIT_CODE
print "end"
}
> nonzero
start
if you use complete you can get the exit code. I typically use -i to ignore errors.
❯ do -i { bash -c false } | complete
╭───────────┬───╮
│ stdout │ │
│ stderr │ │
│ exit_code │ 1 │
╰───────────┴───╯
@fdncred please see my earlier comment:
I think it would be nice (though I don't know how the implementation would go) if
do -ianddo -cwould preserve the exit code in some way, or if there was a separate flag that did that. My use case is that I want the numeric exit code from an external command, but don't want to usecompleteas it prevents streaming the stdout.
tl;dr I need the streaming stdout so I can't use complete, and I need the exit code so I can't use do -i or do -c or try-catch
I guess we'd have to have a list of exit codes. Not sure I'm a fan of that.