如果 bash 脚本或它创建的任何后台作业发生错误,则退出该脚本
Exit a bash script if an error occurs in it or any of the background jobs it creates
背景
我正在编写一个 bash 脚本来自动构建位于同一目录中的六个项目的过程。每个项目都有两个脚本 运行 以便构建它:
npm install
npm run build
第一行将从 npm 获取所有依赖项。由于此步骤花费的时间最长,并且项目可以同时获取它们的依赖项,因此我使用后台作业来并行获取所有内容。 (即:npm install &
)
第二行将使用这些依赖项来构建项目。因为这必须在所有步骤 1 完成后发生,所以我在两者之间 运行ning wait
命令。请参阅下面的代码片段。
问题
我希望在任何后台作业或随后发生的 npm run build
步骤发生错误时立即退出我的脚本。
我正在使用 set -e
,但这不适用于后台作业,因此如果一个项目无法安装它的依赖项,其他一切都会继续。
这是我的脚本现在的样子的简化示例。
build.sh
set -e
DIR=$PWD
for dir in ./projects/**/
do
echo -e "3[4;32mInstalling $dir3[0m"
cd $dir
npm install & # takes a while, so do this in parallel
cd $DIR
done
wait # continue once the background jobs are completed
for dir in ./projects/**/
do
cd $dir
echo -e "3[4;32mBuilding $dir3[0m"
npm run build # Some projects use other projects, so build them in series
cd $DIR
echo -e "\n"
done
同样,如果任何时候发生错误,我不想继续在脚本中执行任何操作,这适用于父作业和后台作业。这可能吗?
收集后台作业的 PID;然后,使用 wait
收集每个的退出状态,在该循环中轮询的任何 PID 第一次为非零时退出。
install_pids=( )
for dir in ./projects/**/; do
(cd "$dir" && exec npm install) & install_pids+=( $! )
done
for pid in "${install_pids[@]}"; do
wait "$pid" || exit
done
上面虽然简单,但有一个警告:如果列表中的项目 late 在列表中较早的项目之前退出非零,直到列表中的点被轮询。要解决此警告,您可以重复遍历整个列表:
install_pids=( )
for dir in ./projects/**/; do
(cd "$dir" && exec npm install) & install_pids+=( $! )
done
while (( ${#install_pids[@]} )); do
for pid_idx in "${!install_pids[@]}"; do
pid=${install_pids[$pid_idx]}
if ! kill -0 "$pid" 2>/dev/null; then # kill -0 checks for process existance
# we know this pid has exited; retrieve its exit status
wait "$pid" || exit
unset "install_pids[$pid_idx]"
fi
done
sleep 1 # in bash, consider a shorter non-integer interval, ie. 0.2
done
但是,由于这种轮询,会产生额外的开销。这可以通过捕获 SIGCHLD 并在触发陷阱时引用 jobs -n
(以获取自上次轮询以来状态发生变化的作业列表)来避免。
Bash 不适用于此类并行处理。为了完成你想要的,我不得不写一个函数库。如果可能的话,我建议寻找一种更适合这种情况的语言。
循环遍历 pid 的问题,比如这个...
#!/bin/bash
pids=()
f() {
sleep
echo "no good"
false
}
t() {
sleep
echo "good"
true
}
t 3 &
pids+=$!
f 1 &
pids+=$!
t 2 &
pids+=$!
for p in ${pids[@]}; do
wait $p || echo failed
done
问题是 "wait" 将等待第一个 pid,如果其他 pid 在第一个 pid 完成之前完成,您将无法捕获退出代码。上面的代码在 bash v4.2.46 上显示了这个问题。 false 命令应该产生永远不会被捕获的输出。
背景
我正在编写一个 bash 脚本来自动构建位于同一目录中的六个项目的过程。每个项目都有两个脚本 运行 以便构建它:
npm install
npm run build
第一行将从 npm 获取所有依赖项。由于此步骤花费的时间最长,并且项目可以同时获取它们的依赖项,因此我使用后台作业来并行获取所有内容。 (即:npm install &
)
第二行将使用这些依赖项来构建项目。因为这必须在所有步骤 1 完成后发生,所以我在两者之间 运行ning wait
命令。请参阅下面的代码片段。
问题
我希望在任何后台作业或随后发生的 npm run build
步骤发生错误时立即退出我的脚本。
我正在使用 set -e
,但这不适用于后台作业,因此如果一个项目无法安装它的依赖项,其他一切都会继续。
这是我的脚本现在的样子的简化示例。
build.sh
set -e
DIR=$PWD
for dir in ./projects/**/
do
echo -e "3[4;32mInstalling $dir3[0m"
cd $dir
npm install & # takes a while, so do this in parallel
cd $DIR
done
wait # continue once the background jobs are completed
for dir in ./projects/**/
do
cd $dir
echo -e "3[4;32mBuilding $dir3[0m"
npm run build # Some projects use other projects, so build them in series
cd $DIR
echo -e "\n"
done
同样,如果任何时候发生错误,我不想继续在脚本中执行任何操作,这适用于父作业和后台作业。这可能吗?
收集后台作业的 PID;然后,使用 wait
收集每个的退出状态,在该循环中轮询的任何 PID 第一次为非零时退出。
install_pids=( )
for dir in ./projects/**/; do
(cd "$dir" && exec npm install) & install_pids+=( $! )
done
for pid in "${install_pids[@]}"; do
wait "$pid" || exit
done
上面虽然简单,但有一个警告:如果列表中的项目 late 在列表中较早的项目之前退出非零,直到列表中的点被轮询。要解决此警告,您可以重复遍历整个列表:
install_pids=( )
for dir in ./projects/**/; do
(cd "$dir" && exec npm install) & install_pids+=( $! )
done
while (( ${#install_pids[@]} )); do
for pid_idx in "${!install_pids[@]}"; do
pid=${install_pids[$pid_idx]}
if ! kill -0 "$pid" 2>/dev/null; then # kill -0 checks for process existance
# we know this pid has exited; retrieve its exit status
wait "$pid" || exit
unset "install_pids[$pid_idx]"
fi
done
sleep 1 # in bash, consider a shorter non-integer interval, ie. 0.2
done
但是,由于这种轮询,会产生额外的开销。这可以通过捕获 SIGCHLD 并在触发陷阱时引用 jobs -n
(以获取自上次轮询以来状态发生变化的作业列表)来避免。
Bash 不适用于此类并行处理。为了完成你想要的,我不得不写一个函数库。如果可能的话,我建议寻找一种更适合这种情况的语言。
循环遍历 pid 的问题,比如这个...
#!/bin/bash
pids=()
f() {
sleep
echo "no good"
false
}
t() {
sleep
echo "good"
true
}
t 3 &
pids+=$!
f 1 &
pids+=$!
t 2 &
pids+=$!
for p in ${pids[@]}; do
wait $p || echo failed
done
问题是 "wait" 将等待第一个 pid,如果其他 pid 在第一个 pid 完成之前完成,您将无法捕获退出代码。上面的代码在 bash v4.2.46 上显示了这个问题。 false 命令应该产生永远不会被捕获的输出。