添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
贪玩的上铺  ·  Windows ...·  1 周前    · 
仗义的夕阳  ·  java ...·  1 月前    · 
孤独的铁链  ·  Python MySQL – ...·  1 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

All of my scripts have errexit turned on; that is, I run set -o errexit . However, sometimes I want to run commands like grep , but want to continue execution of my script even if the command fails.

How do I do this? That is, how can I get the exit code of a command into a variable without killing my whole script?

I could turn errexit off, but I'd prefer not to.

Keeping set -e off is really the best thing . Automatic error handling is only more benefit than cost when its behavior is consistent, predictable and reliable, and set -e is none of those things: It varies between shells, between releases of the same shell, and has numerous hidden surprises: For instance, if you're writing a function, whether errexit takes effect on an error on any command within it depends on whether the exit status of the function itself is being tested. Charles Duffy May 20, 2017 at 1:54 @CharlesDuffy The only alternatives are the shell running off the rails after an error, ruining my servers, or adding || exit $? after each command. The code maintenance overhead due to -e drawbacks that you describe is incomparably smaller than that. ivan_pozdeev Jan 9, 2018 at 13:44 @ivan_pozdeev, first, it's just || exit (when not in a function context where || return is desirable instead); $? is the default exit status. Second, foo || exit after each and every command is clearly not the right thing -- if it were, the behavior of set -e wouldn't have such a large (and varied, between shells) set of exceptions in its behavior, making its behavior contextually-sensitive and hard to predict. For instance, (( ++count )) || exit is clearly wrong since it exits when incrementing count from 0 to 1 , yet set -e; (( ++count )) does precisely that. Charles Duffy Jan 9, 2018 at 16:09 ...or, rather, does precisely that in some versions of bash but not others , which further reinforces my point: If you can't rely on the behavior on set -e to be consistent, you're better off putting in explicit || die "error message here" calls ( die being a conventional name for a function that prints an error before exiting), such that you can ensure consistency. Charles Duffy Jan 9, 2018 at 16:14

Your errexit will only cause the script to terminate if the command that fails is "untested". Per man sh on FreeBSD:

         Exit immediately if any untested command fails in non-interactive
         mode.  The exit status of a command is considered to be explic-
         itly tested if the command is part of the list used to control an
         if, elif, while, or until; if the command is the left hand oper-
         and of an ``&&'' or ``||'' operator; or if the command is a pipe-
         line preceded by the ! keyword.

So .. if you were thinking of using a construct like this:

grep -q something /path/to/somefile
retval=$?
if [ $retval -eq 0 ]; then
  do_something  # found
  do_something_else  # not found

you should instead use a construct like this:

if grep -q something /path/to/somefile; then
  do_something  # found
  do_something_else  # not found

The existence of the if keyword makes the grep command tested, thus unaffected by errexit. And this way takes less typing.

Of course, if you REALLY need the exit value in a variable, there's nothing stopping you from using $?:

if grep -q something /path/to/somefile; then
  do_something  # found
  unnecessary=$?
  do_something $unnecessary  # not found
                Great answer. Note the following "trap" expanding on the latter example: if ! false; then e=$?; echo $e; else echo ok; fi prints 0 as the ! operator introduces another evaluation, while if false; then echo ok; else e=$?; echo $e; fiprints 1 as expected.
– Alexandre Gattiker
                Dec 14, 2019 at 19:30

Here's a way to achieve this: you can "turn off" set -o errexit for some lines of code, and then turn it on again when you decide:

set +e  #disables set -o errexit
grep -i something file.txt
rc="$?" #capturing the return code for last command
set -e #reenables set -o errexit

Another option would be the following:

grep -i something file.txt || rc="$?"

That would allow you to capture the return code on the variable rc, without interrupting your script. You could even extend this last option to capture and process the return code on the same line without risking to trigger an exit:

grep -i something file.txt || rc="$?" && echo rc="$?" > somefile.txt && command || :

The last bit ||: will guarantee that the line above always returns a return code = 0 (true).

In fact, it's the use of the or bars (||) that causes this to work, not the echo of the variable to a text file. The exit status of a command is considered to be explicitly tested if it's on the left hand side of ||. See my answer for the full text from the man page. – ghoti May 20, 2017 at 1:50 Yes, I did not state otherwise (that the use of "or" || is what makes it "work"). However, my comment above is correct, because if the last command fails (in my example echo "$rc" > file.txt), that would still trigger the exit. – Jamil Said May 20, 2017 at 1:53 I think the only problem with your comment is that it doesn't make clear that the command appearing after the semicolon isn't actually part of the same command line containing the ||, it's an entirely different command regardless of its "position" to the right of or below the line in question. If writing to file.txt generates a permission error, that's unrelated to the issue in the question. – ghoti May 20, 2017 at 12:24 @ghoti OK, I see your point now. I changed my answer for sake of clarity, and also added some improvements. – Jamil Said May 20, 2017 at 20:45

A form without variable name repetition -- rc=$(<command> && echo $? || echo $?) -- has an expression rvalue but would also capture the stdout of <command>1. So, it's only safe if you "know" that <command> has no normal output.

Using the a && b || c construct is safe here because rc=$? and $(echo $?) can never fail.

1Sure, you can work around this by fiddling with file descriptors, but that'd make the construct long-winded and inconvenient as a standard

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.