该脚本只是尝试将文件复制foo
到bar/
,如果不成功,则再重试 2 次。
编辑:事实证明,第二个示例有效而第一个示例无效的原因是,在or分支(...)
之后使用 运行一组命令被视为子 shell,并且不会影响外部变量。&&
||
所以修改后的问题:如何将一些命令(如第一个示例)分组,但不使用子 shell 来执行此操作?
编辑2:答案是替换( ... )
为{ ...; }
!
#!/usr/bin/env bash
#
# Be safe
set -euo pipefail
# Init variables
__file="foo"
output_dir="bar"
logfile="test.log"
errorlevel=8
count=0
# Try three times to copy a file
until (( errorlevel == 0 || count == 3 )); do
cp -Lv --no-preserve=mode \
"$__file" \
"$output_dir/" \
| tee -a "$logfile" \
&& (errorlevel=$?; echo "zero return, $errorlevel") \
|| (errorlevel=$?; echo "non-zero return, $errorlevel")
echo "errorlevel $errorlevel"
(( ++count ))
echo "count $count"
done
unset errorlevel
unset count
问题:
当复制失败或成功时,||
或&&
分支会正确捕获并将变量分别设置errorlevel
为1
或0
,如echo $errorlevel
分支中包含的 所证明的那样。
但下一个命令(也是回显)返回初始变量值8
!我不明白为什么会发生这种情况。
一些注意事项:
- 我明确使用
&&
and||
是因为我必须set -e
使这个脚本尽可能安全(这实际上是 1500 行脚本中的一个小例程)。如果我不使用||
捕获非零返回,则脚本退出。或者,我可以set +e
执行复制例程,而不必使用&&
and||
(通过捕获错误级别一次),然后set -e
再次使用,但我宁愿不这样做,因为它与我的脚本其余部分的风格不匹配。 - 我用来
set -o pipefail
在管道中传递一个非零错误级别,这样我tee -a "$logfile"
就不会总是覆盖我的 exiterrorlevel
。 - 我使用
++count
而不是count++
因为前者返回零错误级别,而后者返回非零错误级别(并且(( count++ )) || true
由于set -e
注释#1中的解释,我将不得不做一个丑陋的事情)。
我现在已经通过将errorlevel
变量显式设置为1
or来解决这个问题0
,因为我实际上并不关心非零errorlevel
是什么,但我仍然很想知道为什么上面的方法没有按我的预期工作。
这是解决方法:
#!/usr/bin/env bash
#
set -euo pipefail
__file="foo"
output_dir="bar"
logfile="test.log"
errorlevel=1
count=0
until (( errorlevel == 0 || count == 5 )); do
cp -Lv --no-preserve=mode \
"$__file" \
"$output_dir/" \
| tee -a "$logfile" \
&& errorlevel=0 \
|| errorlevel=1
(( ++count ))
sleep 1
done
unset errorlevel
unset count
任何见解都将不胜感激!
答案1
我更喜欢使用if
块:
errorlevel=0
if cp -Lv --no-preserve=mode \
"$__file" \
"$output_dir/" \
| tee -a "$logfile"
then
echo "zero return, $errorlevel"
else
errorlevel=$?
echo "non-zero return, $errorlevel"
fi
防止if
非零退出状态触发-e
错误退出操作。
(当然,最初的问题是( )
创建一个子 shell,因此变量赋值永远不会影响父 shell。)