【问题标题】:How to get grep exit code without affecting standard output of piped command如何在不影响管道命令的标准输出的情况下获取 grep 退出代码
【发布时间】:2020-04-28 20:54:16
【问题描述】:

我有这个功能:

psyu() {
    sudo pacman -Syu --noconfirm | grep -q 'non ci sono aggiornamenti'
    [ $? != 0 ] &&
        notify-send -i /usr/share/icons/arch.png "Packages upgraded" ||
        notify-send -i /usr/share/icons/arch.png "Nothing to upgrade or there was an error"
}

(我是意大利人,所以我检查了“没有更新”的意大利语翻译。我不知道该怎么做,因为 pacman 没有针对不同情况的不同退出代码。)

我希望终端中的命令输出正常(可视化所有 pacman stdout),但我也想使用 grep 来使该功能正常工作。

如果有更好的方法,请告诉我。

【问题讨论】:

  • 如果[ $? = 0 ] 是真的,如果不是,你能澄清一下你打算得到什么吗?
  • foo && bar || bazif foo; then bar; else baz; fi 不同。请勿将其用作替代品。

标签: bash shell grep pacman-package-manager


【解决方案1】:

使用临时文件描述符将输出转发到标准输出。不需要使用$?,只需将管道放入if即可。

{
if 
    sudo pacman -Syu --noconfirm |
    tee >(cat >&3) |
    grep -q 'non ci sono aggiornamenti'
then 
    notify-send -i /usr/share/icons/arch.png "Packages upgraded"
else
    notify-send -i /usr/share/icons/arch.png "Nothing ..."
fi
} 3>&1

或者更干净:

if {
       sudo pacman -Syu --noconfirm |
       tee >(cat >&3) |
       grep -q 'non ci sono aggiornamenti'
   } 3>&1
then 
    notify-send -i /usr/share/icons/arch.png "Packages upgraded"
else
    notify-send -i /usr/share/icons/arch.png "Nothing ..."
fi

【讨论】:

    【解决方案2】:

    您可以使用tee复制pacman的标准输出,并将其中一份重定向到当前进程的控制tty;在 Linux 上,可以使用 /dev/tty:

    sudo pacman -Syu --noconfirm |
      tee /dev/tty |
      if grep -q 'non ci sono aggiornamenti'; then
        notify-send -i /usr/share/icons/arch.png "Nothing ..."
      else
        notify-send -i /usr/share/icons/arch.png "Packages upgraded"
      fi
    

    或者,您可以使用checkupdates 中的pacman-contrib。当没有可用更新时,其退出状态为2

    checkupdates 1>/dev/null
    if test "$?" -eq 2; then
      message="Nothing to upgrade or there was an error"
    else
      sudo pacman -Syu --noconfirm
      message="Packages upgraded"
    fi
    notify-send -i /usr/share/icons/arch.png -- "$message"
    

    请注意,在您的函数和我的第一个代码 sn-ps 中,if 分支之一(或 AND/OR 列表中的一个命令)总是执行,当pacman 因错误而终止时,会给您一个误导性通知。
    还要考虑到这一点,您需要将pacman 的输出副本发送到临时文件,因为您无法捕获pacman 的退出状态并且消耗(grep)其在同一管道中输出:

    psyu () (
      set -o pipefail
      trap 'rm -rf -- "$tmpdir"' EXIT
      tmpdir=$(mktemp -d)
      tmpfile="$tmpdir/pacman.out"
      if ! sudo pacman -Syu --noconfirm |
        tee -- "$tmpfile"
      then
        message="pacman: Some error occurred"
      elif grep -q -- 'non ci sono aggiornamenti' "$tmpfile"; then
        message="Nothing to upgrade"
      else
        message="Packages upgraded"
      fi
      notify-send -i /usr/share/icons/arch.png -- "$message"
    )
    

    需要pipefail shell 选项以允许if 测试也捕获管道第一阶段发生的错误(否则其退出状态将是最后一个命令的退出状态)。
    最后,请注意,函数定义在括号中的 () 是为了避免在调用 shell 中设置陷阱和选项。

    【讨论】:

      【解决方案3】:

      我相信你可以在这里使用 awk:

      sudo pacman -Syu --noconfirm|
        awk '1;/non ci sono aggiornamenti/{r=7};END{exit(r)}'
      [ $? != 7 ] && notify-send -i /usr/share/icons/arch.png "Packages upgraded" ||
        notify-send -i /usr/share/icons/arch.png "Nothing to upgrade or there was an error"
      

      awk 的意思是:打印来自 pacman 的所有行(这是唯一的 1 所做的)。如果输出中存在“non ci sono aggiornamenti”,则设置r=7。最后,终止并返回退出代码(如果找到匹配则返回 7,否则返回 0)。

      我在那里使用了 7,因为该错误不太可能用于指示另一个常见错误,但原则上它可以是任何非零值。

      【讨论】:

      • 为什么要使用 awk 而不是 bash 的本机模式匹配?
      • 即:pacman_output=$(sudo pacman -Syu --noconfirm); pacman_retval=$?; if [[ $pacman_output =~ "your substring here" ]]; then ...thing to do if the substring is found...; else ...thing to do if the substring is not found...; fi——那么返回值的测试和输出的测试之间没有混淆(这可能应该是对 stderr 的测试,但这是 OP 的问题)。
      • @CharlesDuffy 哦,我明白了,您会将所有输出放在一个变量中。我只是对此有一种本能的排斥,但我认为在这种情况下它是有效的,也许值得你回答?
      猜你喜欢
      • 2015-11-21
      • 2015-05-09
      • 1970-01-01
      • 1970-01-01
      • 2012-06-25
      • 2018-11-04
      • 2016-09-04
      • 1970-01-01
      • 2011-10-15
      相关资源
      最近更新 更多