Bash:在源脚本中停止错误

时间:2014-06-27 20:44:22

标签: bash shell

在要执行的shell脚本中,我可以使用set -e中止错误。

但是,在源脚本中,如果稍后的命令以错误状态退出,则使用set -e将终止原始shell。

source set_e.sh
./exit_1.sh
# shell dies

一个简单的解决方案是在脚本结尾处set +e,但是如果使用的话,这将破坏父set -e(如果有人将我的脚本包裹在未来)。

如何在源代码脚本中获得错误中止功能?

6 个答案:

答案 0 :(得分:4)

这是不可能的。但是,如果您愿意,可以选择使用子shell:

(
    set -e
    source another.sh
)

只有调用脚本的环境永远不会被被调用的脚本改变。

注意:使用换行符分隔两个命令并不使用分号可能很重要。

答案 1 :(得分:3)

set -e更改为return 0(或选择您喜欢的整数,而不是0)。您可以将其视为将源文件视为函数。例如:

$ cat myreturn.sh
#!/bin/bash

let i=0

while test "$i" -lt 10; do

    echo "i $i"
    if test "$i" -gt 5 ; then
        return 5
    fi
    sleep 1
    ((i+=1))

done

return 4

$ ( . myreturn.sh )
i 0
i 1
i 2
i 3
i 4
i 5
i 6

$ echo $?
5

答案 2 :(得分:3)

嗯,问题不是很清楚:原始作者在拦截源脚本中的错误后想要的是什么,但是,作为解决方案的入口点,以下就足够了:

您可以在ERR上设置陷阱并在那里处理源脚本中的错误。下面是两个场景:一个使用源代码脚本使用“set -e”,另一个源代码脚本使用“set -e”。

主脚本使用已定义的“set -e”调用辅助脚本并捕获错误:

[galaxy => ~]$ cat primary.sh
#!/bin/sh

set -e
echo 'Primary script'
trap 'echo "Got an error from the secondary script"' ERR
source secondary.sh
trap - ERR
echo 'Primary script exiting'
[galaxy => ~]$ cat secondary.sh
#!/bin/sh

echo 'Secondary script'
set -e
echo 'Secondary script generating an error'
false
echo 'Secondary script - should not be reached'
[galaxy => ~]$ ./primary.sh
Primary script
Secondary script
Secondary script generating an error
Got an error from the secondary script
[galaxy => ~]$

主脚本在没有“set -e”的情况下调用辅助脚本并捕获错误:

[galaxy => ~]$ cat primary.sh
#!/bin/sh

set -e
echo 'Primary script'
trap 'echo "Got an error from the secondary script"' ERR
source secondary.sh
trap - ERR
echo 'Primary script exiting'
[galaxy => ~]$ cat secondary.sh
#!/bin/sh

echo 'Secondary script'
echo 'Secondary script generating an error'
false
echo 'Secondary script - should not be reached if sourced by primary.sh'
[galaxy => ~]$ ./primary.sh
Primary script
Secondary script
Secondary script generating an error
Got an error from the secondary script
[galaxy => ~]$

作为奖励:截取源脚本中的错误并继续:

[galaxy => ~]$ cat primary.sh
#!/bin/sh

echo 'Primary script'
i=0
while [ $i = 0 ]; do
    i=1
    trap 'echo "Got an error from the secondary script"; break' ERR
    source secondary.sh
done
trap - ERR
echo 'Primary script exiting'
[galaxy => ~]$ cat secondary.sh
#!/bin/sh

echo 'Secondary script'
echo 'Secondary script generating an error'
false
echo 'Secondary script - should not be reached if sourced by primary.sh'
[galaxy => ~]$ ./primary.sh
Primary script
Secondary script
Secondary script generating an error
Got an error from the secondary script
Primary script exiting
[galaxy => ~]$

答案 3 :(得分:2)

您可以检查set -e是否已启用并在之后有条件地设置它:

[[ $- == *e* ]] && state=-e || state=+e
set -e
yourcode
set "$state"

但请注意,set -e是错误的,脚本永远不应该依赖它来保证正确性。例如,如果有人在if语句中提供您的脚本,set -e可能无法再正常运行:

echo '
 set -e
 ls file.that.doesnt.exist
 echo "Success"
' > yourscript

set -e
if ! source yourscript
then 
  echo "Initialization failed"
fi
echo "Done"

然后{b} 4.3.30中的set -e失败会导致yourscript失败。

ls: cannot access file.that.doesnt.exist: No such file or directory
Success
Done

虽然它将在bash 2,3和最多4.2中退出整个脚本:

ls: cannot access file.that.doesnt.exist: No such file or directory

答案 4 :(得分:1)

设置、捕捉和取消设置 set -e 对我来说相当脆弱。 因此,我更喜欢捕捉错误。

这是我经常使用的单行,包括尝试静音 return(如果来源有效),否则 exit。另外,取消设置陷阱,否则就会留下源脚本。

trap 'echo Error $? on line $LINENO; trap - ERR; return 2>/dev/null || exit' ERR

答案 5 :(得分:0)

您可以通过set -o检测是否在源脚本的开头设置了errexit选项,并在源脚本的末尾恢复其原始值。