Shell 在 for 循环中的算术表达式处静默退出

gzc*_*gzc 8 shell bash arithmetic

我编写了一个脚本来 ssh 远程主机,执行命令,将输出保存到文件,并检查输出。但是(( success++ ))当迭代 array 中的第一项时,它总是在 line 处静默退出workers。如果我替换(( success++ ))echo "process $worker",它将正常工作并打印所有主机。我无法弄清楚出了什么问题。

#!/bin/bash

set -x
set -e
workers=('host-1' 'host-2' 'host-3')

output_dir=$(mktemp -d)

for worker in ${workers[@]}; do
  ssh $worker '
    echo abc
    echo OK
  ' > "$output_dir/$worker" &
done

echo "waiting..."
sleep 3
wait

success=0
regexp='OK$'
for worker in ${workers[@]}; do
  output=`cat "$output_dir/$worker"`
  if [[ "$output" =~ $regexp ]]; then
    (( success++ ))
  fi
done

echo "Total ${#workers[@]}; success: $success; failure: $((${#workers[@]} - success))"
Run Code Online (Sandbox Code Playgroud)

l0b*_*0b0 13

一个简单的例子应该可以解释原因:

$ ((success++))
$ echo $?
1
Run Code Online (Sandbox Code Playgroud)

原因是任何产生零数值的算术运算都会返回 1我不知道该说什么 - Bash 已经为全世界提供了足够的陷阱。

  • `如果((..)); 如果 `(( .. ))` 没有返回合理的返回值,......` 将无法工作。当然,有人可能会说它只有在存在显式比较时才会失败(比如 `(( i++ < n ))`),但是对零的隐式比较使得像 `while (( i-- ))` 这样的东西在与 C 和其他编程语言中的方式相同。 (2认同)

ImH*_*ere 8

这是-e设置的结果。任何退出代码为 1(非零)的命令都将触发退出。

这个脚本工作正常:

#!/bin/bash
(( success++))
echo "Still going 1 $success"
Run Code Online (Sandbox Code Playgroud)

这不

#!/bin/bash
set -e
(( success++))
echo "Still going 1 $success"
Run Code Online (Sandbox Code Playgroud)

解决方案

最简单的就是去掉set -e线。

如果这不是一个选项,请使用:

(( ++success ))
Run Code Online (Sandbox Code Playgroud)

其他选择:

#!/bin/bash

set -e
success=0
success=$(( success+1 ))
echo "still going 1 $success"

success=0
(( success=success+1 ))
echo "still going 2 $success"

success=0
(( success+=1 ))
echo "still going 3 $success"

success=0
(( ++success ))
echo "still going 4 $success"

success=0
(( success++ ))
echo "still going 5 $success"
Run Code Online (Sandbox Code Playgroud)

只有选项编号 5 的退出代码为 1。

其他(任何变量值的更复杂的解决方案a)。
第一个使用(POSIX) 冒号 ( :) 内置使其与 POSIX 兼容。

: $(( a+=1 ))        ; echo "6 $a $?"   ## Valid Posix
   (( a++ )) || true ; echo "7 $a $?"
   (( a++ )) || :    ; echo "8 $a $?"
   (( a++ , 1 ))     ; echo "9 $a $?"
   (( a++ | 1 ))     ; echo "10 $a $?"
Run Code Online (Sandbox Code Playgroud)

  • 当你从零开始计数时,这些工作,但在一般情况下,你需要用类似`(( something... )) || 之类的东西来压缩错误退出。真的` (2认同)