Bash 检查while循环中调用的程序的退出代码

Bash 检查while循环中调用的程序的退出代码,bash,shell,Bash,Shell,我想在bash中编写一个循环,直到某个命令停止失败为止(返回非零退出代码),如下所示: while ! my_command; do # do something done 但是在这个循环中,我需要检查返回的退出代码my_命令,所以我尝试了以下方法: while ! my_command; do if [ $? -eq 5 ]; then echo "Error was 5" else echo "Error was not 5"

我想在bash中编写一个循环,直到某个命令停止失败为止(返回非零退出代码),如下所示:

while ! my_command; do
    # do something
done
但是在这个循环中,我需要检查返回的退出代码
my_命令
,所以我尝试了以下方法:

while ! my_command; do
    if [ $? -eq 5 ]; then
        echo "Error was 5"
    else
        echo "Error was not 5"
    fi
    # potentially, other code follows...
done
但是,特殊变量
在循环体中变为
0
。 显而易见的解决办法是:

while true; do
    my_command
    EC=$?
    if [ $EC -eq 0 ]; then
        break
    fi
    some_code_dependent_on_exit_code $EC
done

如何在循环体中检查
my_命令
(称为循环头)的退出代码,而不使用
while true
循环(如上图所示的中断条件)?

如果
true
命令伤害了您的敏感性,您可以写:

while my_command ; ret=$? ; [ $ret -ne 0 ];do
    echo do something with $ret
  done
这可以简化:

while my_command ; ((ret=$?)) ;do
    echo do something with $ret
  done
但如果您不需要ResultCode,您可以简单地:

while my_command ; [ $? -ne 0 ];do
    echo Loop on my_command
  done

也许,为什么不呢

while ! my_command ;do
    echo Loop on my_command
  done

但是从那里你可以更好地使用除了众所周知的
while
循环之外,POSIX还提供了
直到
循环,消除了否定
我的_命令退出状态的需要

# To demonstrate
my_command () { read number; return $number; }

until my_command; do
    if [ $? -eq 5 ]; then
        echo "Error was 5"
    else
        echo "Error was not 5"
    fi
    # potentially, other code follows...
done

您可以从
PIPESTATUS
内置变量中获取否定命令的状态:

while ! my_command ; do
    some_code_dependent_on_exit_code "${PIPESTATUS[0]}"
done

chepner的解决方案在这种情况下更好,但是对于类似的问题,
PIPESTATUS
有时很有用。

因此在我的情况下,我还需要忽略一些退出代码,并希望向用户提供一些有用的输出,因此我写了以下内容:

retrycmd(){
  MSG=$1
  IGNORE=$2
  shift 2
  local SLEEP_T=5
  local L_CNT=5
  local C_CNT=0
  while ((C_CNT++ < ${L_CNT})) && ! $@;do
    RET=${PIPESTATUS[0]}
    #echo "RET: ${RET}"
    for I in ${IGNORE//,/ };do # bashism: replace(/) all(/) match(,) with(/) value(<space>)
      if ((${RET} == ${I}));then
        #echo "${RET} = ${I}"
        break 2
      fi
    done
    echo "${MSG} failure ${C_CNT}"
    sleep ${SLEEP_T}
  done
  if ((${C_CNT} > ${L_CNT}));then
    echo "${MSG} failed"
    poweroff
  fi
}

#retrycmd "Doing task" "IGNORE,CSV" <CMD>
retrycmd "Ping google" "0" ping www.google.com
retrycmd(){
味精=$1
忽略=2美元
班次2
局部睡眠\u T=5
局部L_CNT=5
局部C_CNT=0
而((C_CNT++<${L_CNT}))&&&!$@;do
RET=${PIPESTATUS[0]}
#回显“RET:${RET}”
对于${IGNORE/,/};do#bashism中的I:replace(/)all(/)match(,)为(/)value()
如果(${RET}=${I}));那么
#回声“${RET}=${I}”
突破2
fi
完成
回显“${MSG}故障${C_CNT}”
睡眠${sleep\u T}
完成
如果(${C_CNT}>${L_CNT})),那么
回显“${MSG}失败”
断电
fi
}
#retrycmd“正在执行任务”“忽略,CSV”
retrycmd“Ping google”0“Ping www.google.com

回答得好!我从来不知道,
当command1;命令2;做…
works在我看来是迄今为止的赢家。我喜欢!当你知道有一个
until
循环时,这是非常明显的!RTFM给我。我认为这是最好的答案,很有道理。我不知道
PIPESTATUS
在这种情况下也起作用。这是。
retrycmd(){
  MSG=$1
  IGNORE=$2
  shift 2
  local SLEEP_T=5
  local L_CNT=5
  local C_CNT=0
  while ((C_CNT++ < ${L_CNT})) && ! $@;do
    RET=${PIPESTATUS[0]}
    #echo "RET: ${RET}"
    for I in ${IGNORE//,/ };do # bashism: replace(/) all(/) match(,) with(/) value(<space>)
      if ((${RET} == ${I}));then
        #echo "${RET} = ${I}"
        break 2
      fi
    done
    echo "${MSG} failure ${C_CNT}"
    sleep ${SLEEP_T}
  done
  if ((${C_CNT} > ${L_CNT}));then
    echo "${MSG} failed"
    poweroff
  fi
}

#retrycmd "Doing task" "IGNORE,CSV" <CMD>
retrycmd "Ping google" "0" ping www.google.com