bash:错误处理和功能

时间:2015-11-14 01:03:46

标签: bash

我试图在一个循环中调用一个函数,然后优雅地处理并在它抛出时继续。

如果我省略|| handle_error,它就会像预期的那样停止整个脚本。

如果我离开foo is fine,则会在错误后打印handle_error,并且根本不会执行#!/bin/bash set -e things=(foo bar) function do_something { echo "param: $1" # just throw on first loop run # this statement is just a way to selectively throw # not part of a real use case scenario where the command(s) # may or may not throw if [[ $1 == "foo" ]]; then throw_error fi # this line should not be executed when $1 is "foo" echo "$1 is fine." } function handle_error { echo "$1 failed." } for thing in ${things[@]}; do do_something $thing || handle_error $thing done echo "done" 。这也是一种预期的行为,它只是它的运作方式。

param: foo
./test.sh: line 12: throw_error: command not found
foo is fine.
param: bar
bar is fine.
done

产量

param: foo
./test.sh: line 12: throw_error: command not found
foo failed.
param: bar
bar is fine.
done

我想拥有的是

do_something

修改

do_something并不需要返回任何内容。它只是抛出一个函数的一个例子,我可能会从示例源代码中删除它,因为我无法控制其内容,也不想对其进行测试,并且测试其中的每个命令是不可行的。

修改

您无法触及make逻辑。我之前说过,它只是一个包含一组可能引发错误的指令的函数。它可能是一个错字,它可能在CI环境中host失败,可能是网络错误。

3 个答案:

答案 0 :(得分:0)

#!/bin/bash

set -e

things=(foo bar)

function do_something() {
  echo "param: $1"
  ret_value=0

  if [[ $1 == "foo" ]]; then
    ret_value=1
  elif [[ $1 == "fred" ]]; then
    ret_value=2
  fi

  echo "$1 is fine."
  return $ret_value
}

function handle_error() {
  echo "$1 failed."
}

for thing in ${things[@]}; do
  do_something $thing || handle_error $thing
done

echo "done"

请参阅上面的评论以获得解释。您无法在不创建返回值的情况下测试返回值,这应该是显而易见的。并且||测试返回值,基本上是一个大于0的值。像&&测试为0.我认为这或多或少是正确的。我相信bash返回值限制是254?我想说。必须是0到254之间的整数。不能是字符串,浮点数等

http://tldp.org/LDP/abs/html/complexfunct.html

  

函数返回一个值,称为退出状态。这类似于   命令返回的退出状态。退出状态可能是   由return语句显式指定,否则它是退出   函数中最后一个命令的状态(如果成功,则为0,以及a   如果不是非零错误代码)。此退出状态可用于   脚本通过引用它作为$?。这种机制有效地允许   脚本函数具有"返回值"类似于C函数。

实际上,foo会返回127命令未找到错误。我认为。我必须进行测试才能确定。

[更新} 不,echo是最后一个命令,正如您从输出中看到的那样。并且最后一个回声的结果是0,所以函数返回0.所以你想要抛弃这个概念并转到像陷阱这样的东西,假设你不能触及函数的内部,很奇怪。

echo fred; echo reval: $?
fred
reval: 0

What does set -e mean in a bash script?

-e  Exit immediately if a command exits with a non-zero status.
  

但它不是很可靠,被认为是一种不好的做法,更好地使用:

trap 'do_something' ERR

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html看到陷阱,这可能会做你想要的。但不是||,除非你添加回报。

答案 1 :(得分:0)

我找到的解决方案是将函数保存在单独的文件中并在子shell中执行。缺点是我们失去了所有当地人。

<强> do-something.sh

2015-11-14T02:42:05.497208+00:00 heroku[api]: Deploy 63ae6bd by email@gmail.com
2015-11-14T02:42:05.497305+00:00 heroku[api]: Release v4 created by email@gmail.com
2015-11-14T02:42:05.651545+00:00 heroku[slug-compiler]: Slug compilation started
2015-11-14T02:42:05.651555+00:00 heroku[slug-compiler]: Slug compilation finished
2015-11-14T02:42:05.820048+00:00 heroku[web.1]: State changed from up to starting
2015-11-14T02:42:08.998026+00:00 heroku[web.1]: Stopping all processes with SIGTERM
2015-11-14T02:42:09.746422+00:00 heroku[web.1]: Starting process with command `vendor/bin/heroku-php-apache2`
2015-11-14T02:42:09.909064+00:00 app[web.1]: Going down, terminating child processes...
2015-11-14T02:42:10.844965+00:00 heroku[web.1]: Process exited with status 0
2015-11-14T02:42:12.439647+00:00 app[web.1]: Starting php-fpm...
2015-11-14T02:42:12.243079+00:00 app[web.1]: Optimizing defaults for 1X dyno...
2015-11-14T02:42:12.435588+00:00 app[web.1]: 4 processes at 128MB memory limit.
2015-11-14T02:42:14.442033+00:00 app[web.1]: Starting httpd...
2015-11-14T02:42:14.672772+00:00 heroku[web.1]: State changed from starting to up
2015-11-14T02:44:38.663711+00:00 heroku[router]: at=info method=GET path="/" host=mywebsite.herokuapp.com request_id=45aae2bd-a6f8-4023-86cf-6aedd5d23882 fwd="138.110.234.184" dyno=web.1 connect=12ms service=2ms status=500 bytes=224
2015-11-14T02:44:38.663113+00:00 app[web.1]: [14-Nov-2015 02:44:38 UTC] PHP Parse error:  syntax error, unexpected '?' in /app/index.php on line 1
2015-11-14T02:44:38.663580+00:00 app[web.1]: 10.146.233.83 - - [14/Nov/2015:02:44:38 +0000] "GET / HTTP/1.1" 500 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36

<强> my-script.sh

#!/bin/bash

set -e

echo "param: $1"

if [[ $1 == "foo" ]]; then
  throw_error
fi

echo "$1 is fine."

产量

#!/bin/bash

set -e

things=(foo bar)

function handle_error {
  echo "$1 failed."
}

for thing in "${things[@]}"; do
  ./do-something.sh "$thing" || handle_error "$thing"
done

echo "done"

如果有更优雅的方式,我会将其标记为正确答案。将在48小时内再次检查。

修改

感谢@PeterCordes评论和this other answer我找到了另一个不需要单独文件的解决方案。

param: foo
./do-something.sh: line 8: throw_error: command not found
foo failed.
param: bar
bar is fine.
done

正确产生

#!/bin/bash

set -e

things=(foo bar)

function do_something {
  echo "param: $1"

  if [[ $1 == "foo" ]]; then
    throw_error
  fi

  echo "$1 is fine."
}

function handle_error {
  echo "$1 failed with code: $2"
}

for thing in "${things[@]}"; do
  set +e; (set -e; do_something "$thing"); error=$?; set -e
  ((error)) && handle_error "$thing" $error
done

echo "done"

答案 2 :(得分:-1)

尝试

if [[ "$1" == "foo" ]]; then
  foo
fi

我想知道它是否试图在条件测试中执行命令foo

来自bash参考:

  

-e       如果管道(参见管道)可能包含一个简单命令(参见简单命令),列表(参见列表),则立即退出   或复合命令(请参阅复合命令)返回非零值   状态。如果失败的命令是shell的一部分,则shell不会退出   紧跟一段时间或直到关键字的命令列表,部分   if语句中的测试,是&amp;&amp ;;中执行的任何命令的一部分。   或||列表除了最后一个&amp;&amp;之后的命令或者||,任何   管道中的命令但是最后一个,或者命令的返回状态   正在被倒置!

如您所见,如果在测试条件中发生错误,则脚本将继续无视并返回0.

-

修改

所以作为回应,我注意到文档继续:

  

如果子shell以外的复合命令返回非零状态   因为命令失败而-e被忽略,所以shell会这样做   不退出。

好吧,因为你的forecho继承,所以没有理由抛出错误!