【问题标题】:Error in this bash script此 bash 脚本中的错误
【发布时间】:2023-06-22 19:38:01
【问题描述】:

我想做一个构建链脚本,如果编译过程中有错误,我不希望它执行到最后。

这是我第一次在 bash 中编写更“详细”的脚本,但它不起作用:

  1. 它不回显 ERROR,尽管我在其中有带有单词 error 的行
  2. 无论testError 的值是多少,脚本都会挂在行中

这是代码:

testError=false

output=$(scons)
while read -r line; do
    if [[ $line == .*[eE]rror.* ]] ; then echo 'ERROR' ; $testError = true ; fi #$testError = true fi
done

echo $testError
if  $testError  ; then exit ; fi;

... other commands

编辑: 关注所有发帖者的回答和Bash setting a global variable inside a loop and retaining its value -- Or process substituion for dummiesHow do I use regular expressions in bash scripts?, 这是代码的最终版本。 它有效:

testError=false

shopt -s lastpipe
scons | while read -r line; do
  if [[ $line =~ .*[eE]rror.* ]] ; then
    echo -e 'ERROR' 
    testError=true 
  fi 
  echo -e '.'
done

if  $testError  ; then
    set -e 
fi

【问题讨论】:

    标签: bash


    【解决方案1】:

    您在管道诱导的子shell 中设置testError 的值。当该子外壳退出时(在管道末端),您所做的任何更改都会消失。试试这个:

    while read -r line; do
       if [[ $line == .*[eE]rror.* ]] ; then
         echo -e 'ERROR' 
         testError=true 
       fi #$testError = true fi
    done < <( scons )
    

    或者,如果您不想或不能使用进程替换,请使用临时文件

    scons > tmp
    while read -r line; do
      if [[ $line == .*[eE]rror.* ]] ; then
        echo -e 'ERROR' 
        testError=true 
      fi #$testError = true fi
    done < tmp
    

    这消除了管道,因此对 testError 的更改在 while 循环之后仍然存在。

    而且,如果您的 bash 版本足够新(4.2 或更高版本),则有一个选项允许管道末尾的 while 循环在当前 shell 中执行,而不是在子 shell 中执行。

    shopt -s lastpipe
    scons | while read -r line; do
      if [[ $line == .*[eE]rror.* ]] ; then
        echo -e 'ERROR' 
        testError=true 
      fi #$testError = true fi
    done
    

    【讨论】:

    • 如果有 4.2.24 版本,可能会这样做。
    【解决方案2】:

    你应该试试

    set -e
    

    如果命令以非零状态退出,这将停止脚本继续运行

    或更好

    error_case() { # do something special; }
    trap 'echo >&2 "an error occurs"; error_case' ERR
    

    每次命令以非零状态退出时都会运行error_case函数

    http://mywiki.wooledge.org/BashFAQ/105

    【讨论】:

      【解决方案3】:

      另一个错误是作业中有空格。并跳过$

      $testError = true
      

      应该是

      testError=true
      

      编辑

      testerror 在子shell 中被更改。试试

      testerror=$(
          scons | while read -r line; do
              if [[ $line == .*[eE]rror.* ]] ; then
                  echo true 
              fi #$testError = true fi
          done
      )
      

      【讨论】:

        【解决方案4】:

        您是否尝试解析 scons 的输出?

        这个:

        output=$(scons)
        while read -r line; do
            if [[ $line == .*[eE]rror.* ]] ; then 
                echo 'ERROR' 
                testError=true
            fi
        done
        

        不这样做。也许你想要:

        scons | while read -r line; do ... ; done
        

        【讨论】:

        • 管道创建一个子shell,其中testError的更改不再是全局的。
        • @chepner 我正在阅读这个问题,与您所指的内容有关:*.com/questions/9012841/…
        • @chepner testError 不需要是全局的;它只需要在循环中被引用。
        • 不,他正在在循环之外显式测试值。
        • @chepner 但他不应该!代码可以大大简化,这个答案只是为了指出他的主要错误:即他没有解析 scons 的输出。
        【解决方案5】:

        我回答也是因为其他答案没有注意到:使用正则表达式应该这样做,使用=~而不是==

        if [[ $line =~ .*[eE]rror.* ]] ; then
        ...
        

        cfHow do I use regular expressions in bash scripts?

        【讨论】: