【问题标题】:Script exits with error when var=$(... | grep "value") is empty, but works when grep has results当 var=$(... | grep "value") 为空时脚本退出并出错,但在 grep 有结果时有效
【发布时间】:2019-08-28 17:44:57
【问题描述】:

当我启用set -o errexit 并且代码中的变量为空时,我有以下 bash 代码(在 Red Hat 上运行)退出,但在设置变量时工作正常;该代码旨在测试是否存在匹配.monitor_* 的屏幕会话,如果存在则执行操作。

我开启了以下功能:

set -o errexit
set -x xtrace; PS4='$LINENO: '

如果存在与上述模式匹配的会话,则它可以工作;但是,如果没有匹配项,它只会退出,除了 xtrace 的以下输出之外没有任何信息

someuser:~/scripts/tests> ./if_test.sh
+ ./if_test.sh
+ PS4='$LINENO: '
4: set -o errexit
5: set -o pipefail
6: set -o nounset
88: /usr/bin/ls -lR /var/run/uscreens/S-storage-rsync
88: grep '.monitor_*'
88: awk '{ print $9 }'
88: /usr/bin/grep -Ev 'total|uscreens'
8: ms=

我测试了我用来设置ms var 的命令,它与xtrace 输出一致,它没有设置。

someuser:~/scripts/tests> test -n "${mn}"
+ test -n ''

我尝试使用 select 语句并得到相同的结果...我无法弄清楚,有人能帮忙吗?谢谢。

我阅读了所有可能的解决方案建议,似乎没有任何解决我的问题。

代码:

#!/usr/bin/env bash

set -o xtrace; PS4='$LINENO: '
set -o errexit
set -o pipefail
set -o nounset

ms="$(/usr/bin/ls -lR /var/run/uscreens/S-"${USER}" | /usr/bin/grep -Ev "total|uscreens" | grep ".monitor_*" | awk '{ print $9 }')"

if [[ -z "${ms}" ]]; then
    echo "Handling empty result"
elif [[ -n "${ms}" ]]; then
    echo "Handling non-empty result"
fi

提出以下答案:Test if a variable is set in bash when using "set -o nounset";但是,它根本没有解决问题。在我的情况下,正在测试的变量已设置,正如我在详细信息中所述,它设置为"",或者什么都没有。谢谢;但是,它没有帮助。

这似乎是它不喜欢的变量声明。

ms="$(/usr/bin/ls -lR /var/run/uscreens/S-"${USER}" | /usr/bin/grep -Ev "total|uscreens" | grep ".monitor_*" | awk '{ print $9 }')"

【问题讨论】:

  • 你正在运行set -o pipefail。当grep 不匹配任何内容时,它的退出状态为非零,而pipefail 则使整个管道失败。这一切都完全按照你告诉你的shell它应该表现的方式。
  • 顺便说一句,在决定使用set -o errexit 之前,我强烈建议先完成BashFAQ #105 中的练习。
  • 好吧,我的 main 建议是不要使用set -e。当然,您始终可以为特定的管道组件添加条件。

标签: linux bash


【解决方案1】:
  • 您正在运行set -o pipefail,因此如果管道中的任何组件具有非零退出状态,则整个管道将被视为具有非零退出状态。
  • 您的管道运行grep。每当找不到匹配项时,grep 的状态为非零。
  • 你正在运行set -o errexit(又名set -e)。启用errexit 后,只要任何命令失败,脚本就会终止(受一系列冗长而复杂的异常的影响;其中一些在the exercises section of BashFAQ #105 中介绍,其他在this excellent reference 中涉及)。

因此,当您的 grep 命令中没有匹配项时,您的脚本将在运行相关管道的命令替换时终止。


如果您想从set -e 的行为中排除特定命令,最简单的方法是简单地附加||:|| true 的简写),将命令标记为“已检查”。

【讨论】:

  • 对于那些仍在学习或需要更多关于查尔斯的答案如何应用于我的代码的详细信息的人,我修改了我的变量以将|| true 应用于它(上面显示的完整变量:... grep ".monitor_*" | awk '{ print $9 }' || true)" 或者, ... grep ".monitor_*" | awk '{ print $9 }' ||:)"
  • 考虑awk '/total|uscreens/ && /.monitor_*/ { print $9 }',完全摆脱greps。
  • ...也就是说,请注意parsing ls output is itself best avoided
【解决方案2】:

“您正在运行 set -o pipefail。当 grep 不匹配任何内容时,它具有非零退出状态,并且使用 pipefail,这会使整个管道失败。这一切都完全按照您告诉您的方式壳它应该表现。” ——查尔斯·达菲

Charles 上面的评论正是发生的事情,我的脚本按预期工作,如果我希望保持 set -o pipefail 设置,我需要调整逻辑以不同方式工作。

感谢您的帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-17
    • 1970-01-01
    • 1970-01-01
    • 2022-11-30
    • 1970-01-01
    • 1970-01-01
    • 2011-05-21
    相关资源
    最近更新 更多