Tho*_*lde 14 shell bash shell-script error-handling
我制作了以下脚本:
# !/bin/bash
# OUTPUT-COLORING
red='\e[0;31m'
green='\e[0;32m'
NC='\e[0m' # No Color
# FUNCTIONS
# directoryExists - Does the directory exist?
function directoryExists {
cd $1
if [ $? = 0 ]
then
echo -e "${green}$1${NC}"
else
echo -e "${red}$1${NC}"
fi
}
# EXE
directoryExists "~/foobar"
directoryExists "/www/html/drupal"
Run Code Online (Sandbox Code Playgroud)
该脚本有效,但除了我的回声之外,还有输出
cd $1
Run Code Online (Sandbox Code Playgroud)
执行失败。
testscripts//test_labo3: line 11: cd: ~/foobar: No such file or directory
Run Code Online (Sandbox Code Playgroud)
有没有可能抓住这个?
Gil*_*il' 14
使用set -e到出口集上错误模式:如果一个简单的命令返回非零状态(表示失败),外壳退出。
请注意,set -e这并不总是有效。测试位置中的命令允许失败(例如if failing_command,failing_command || fallback)。子shell中的命令只会导致退出子shell,而不是父级:set -e; (false); echo foodisplays foo。
或者,或此外,在 bash(以及 ksh 和 zsh,但不是普通 sh)中,您可以指定在命令返回非零状态时执行的命令,带有ERR陷阱,例如trap 'err=$?; echo >&2 "Exiting on error $err"; exit $err' ERR. 请注意,在类似(false); …的情况下,ERR 陷阱是在子 shell 中执行的,因此它不会导致父级退出。
您的脚本在运行时会更改目录,这意味着它不适用于一系列相对路径名。然后您稍后评论说您只想检查目录是否存在,而不是使用的能力
cd,因此根本不需要使用答案cd。修改。使用tput
和颜色来自man terminfo:
#!/bin/bash -u
# OUTPUT-COLORING
red=$( tput setaf 1 )
green=$( tput setaf 2 )
NC=$( tput setaf 0 ) # or perhaps: tput sgr0
# FUNCTIONS
# directoryExists - Does the directory exist?
function directoryExists {
# was: do the cd in a sub-shell so it doesn't change our own PWD
# was: if errmsg=$( cd -- "$1" 2>&1 ) ; then
if [ -d "$1" ] ; then
# was: echo "${green}$1${NC}"
printf "%s\n" "${green}$1${NC}"
else
# was: echo "${red}$1${NC}"
printf "%s\n" "${red}$1${NC}"
# was: optional: printf "%s\n" "${red}$1 -- $errmsg${NC}"
fi
}
Run Code Online (Sandbox Code Playgroud)
(编辑为使用更无懈可击的printf而不是echo可能对文本中的转义序列起作用的有问题
的。)
要扩展@Gilles 的回答:
实际上,set -e如果在命令||之后使用操作符,即使在子 shell 中运行它们,也不能在命令内部工作;例如,这行不通:
#!/bin/sh
# prints:
#
# --> outer
# --> inner
# ./so_1.sh: line 16: some_failed_command: command not found
# <-- inner
# <-- outer
set -e
outer() {
echo '--> outer'
(inner) || {
exit_code=$?
echo '--> cleanup'
return $exit_code
}
echo '<-- outer'
}
inner() {
set -e
echo '--> inner'
some_failed_command
echo '<-- inner'
}
outer
Run Code Online (Sandbox Code Playgroud)
但是||需要操作符来防止在清理之前从外部函数返回。
有一个小技巧可以用来解决这个问题:在后台运行内部命令,然后立即等待它。该wait内建将返回内部命令的退出代码,现在你正在使用||后wait,而不是内部功能,set -e工作正常后内:
#!/bin/sh
# prints:
#
# --> outer
# --> inner
# ./so_2.sh: line 27: some_failed_command: command not found
# --> cleanup
set -e
outer() {
echo '--> outer'
inner &
wait $! || {
exit_code=$?
echo '--> cleanup'
return $exit_code
}
echo '<-- outer'
}
inner() {
set -e
echo '--> inner'
some_failed_command
echo '<-- inner'
}
outer
Run Code Online (Sandbox Code Playgroud)
这是建立在这个想法之上的通用函数。如果删除local关键字,它应该在所有 POSIX 兼容的 shell 中工作,即local x=y用 just替换 all x=y:
# [CLEANUP=cleanup_cmd] run cmd [args...]
#
# `cmd` and `args...` A command to run and its arguments.
#
# `cleanup_cmd` A command that is called after cmd has exited,
# and gets passed the same arguments as cmd. Additionally, the
# following environment variables are available to that command:
#
# - `RUN_CMD` contains the `cmd` that was passed to `run`;
# - `RUN_EXIT_CODE` contains the exit code of the command.
#
# If `cleanup_cmd` is set, `run` will return the exit code of that
# command. Otherwise, it will return the exit code of `cmd`.
#
run() {
local cmd="$1"; shift
local exit_code=0
local e_was_set=1; if ! is_shell_attribute_set e; then
set -e
e_was_set=0
fi
"$cmd" "$@" &
wait $! || {
exit_code=$?
}
if [ "$e_was_set" = 0 ] && is_shell_attribute_set e; then
set +e
fi
if [ -n "$CLEANUP" ]; then
RUN_CMD="$cmd" RUN_EXIT_CODE="$exit_code" "$CLEANUP" "$@"
return $?
fi
return $exit_code
}
is_shell_attribute_set() { # attribute, like "x"
case "$-" in
*"$1"*) return 0 ;;
*) return 1 ;;
esac
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
#!/bin/sh
set -e
# Source the file with the definition of `run` (previous code snippet).
# Alternatively, you may paste that code directly here and comment the next line.
. ./utils.sh
main() {
echo "--> main: $@"
CLEANUP=cleanup run inner "$@"
echo "<-- main"
}
inner() {
echo "--> inner: $@"
sleep 0.5; if [ "$1" = 'fail' ]; then
oh_my_god_look_at_this
fi
echo "<-- inner"
}
cleanup() {
echo "--> cleanup: $@"
echo " RUN_CMD = '$RUN_CMD'"
echo " RUN_EXIT_CODE = $RUN_EXIT_CODE"
sleep 0.3
echo '<-- cleanup'
return $RUN_EXIT_CODE
}
main "$@"
Run Code Online (Sandbox Code Playgroud)
运行示例:
$ ./so_3 fail; echo "exit code: $?"
--> main: fail
--> inner: fail
./so_3: line 15: oh_my_god_look_at_this: command not found
--> cleanup: fail
RUN_CMD = 'inner'
RUN_EXIT_CODE = 127
<-- cleanup
exit code: 127
$ ./so_3 pass; echo "exit code: $?"
--> main: pass
--> inner: pass
<-- inner
--> cleanup: pass
RUN_CMD = 'inner'
RUN_EXIT_CODE = 0
<-- cleanup
<-- main
exit code: 0
Run Code Online (Sandbox Code Playgroud)
使用此方法时,您唯一需要注意的是,从您传递给的命令执行的所有 Shell 变量修改run都不会传播到调用函数,因为该命令在子 shell 中运行。
| 归档时间: |
|
| 查看次数: |
133722 次 |
| 最近记录: |