如何将 if 语句中命令的输出通过管道传递给函数?

How can I pipe output, from a command in an if statement, to a function?

我不知道我在这里尝试的东西是否根本不可能,或者我是否真的缺乏 bash 的语法知识。这是我写的第一个脚本。

我有一个 Nextcloud 实例,我每天使用脚本对其进行备份。我想在脚本运行时将其输出记录到日志文件中。这工作正常,但我想看看我是否也可以将 Nextcloud occ 命令的输出也通过管道传输到日志文件。

我这里有一个 if 语句来检查文件扫描是否失败:

if ! sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all; then
    Print "Error: Failed to scan files. Are you in maintenance mode?"
fi

这很好用,如果系统无法执行命令,我可以处理错误。上面的错误字符串被发送到这个函数:

Print()
{
    if [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "No" ]; then
        echo "" | tee -a "$log_file"
    elif [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "Yes" ]; then
        echo "" >> "$log_file"
    elif [[ "$logging" -eq 0 ]] && [ "$quiet_mode" = "No" ]; then
        echo ""
    fi
}

我怎样才能使 occ 命令的输出也通过管道传输到 Print() 函数,以便它可以记录到控制台和日志文件?

我尝试在 ! 之后使用 | Print 管道命令,但没有成功。

任何帮助将不胜感激,干杯!

这个怎么样?可能有点不正统。

Print()
{
    case $# in
      0) cat;;
      *) echo "$@";;
    esac |
    if [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "No" ]; then
        tee -a "$log_file"
    elif [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "Yes" ]; then
        cat >> "$log_file"
    elif [[ "$logging" -eq 0 ]] && [ "$quiet_mode" = "No" ]; then
        cat
    fi
}

有了这个,您可以

echo "hello mom" | Print

Print "hello mom"

因此您的调用可以重构为

if ! sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all; then
    echo "Error: Failed to scan files. Are you in maintenance mode?"
fi |
Print

明显的缺点是管道进入函数会丢失管道中较早出现的任何故障的退出代码。

对于更传统的方法,保留原始 Print 定义并将调用代码重构为

if output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1); then
    : nothing
else
    Print "error $?: $output"
    Print "Error: Failed to scan files. Are you in maintenance mode?"
fi

我想错误消息将打印到标准错误,而不是标准输出;因此添加 2>&1

我在错误消息中包含了错误代码 $?,以备不时之需。

管道的发送端和接收端必须是一个进程,通常由可执行命令表示。 if 语句不是进程。您当然可以将这样的声明放入流程中。例如,

echo a | ( 
  if true
  then
    cat
  fi )

导致 cata 写入标准输出,因为括号将其放入子进程。

UPDATE:正如评论中指出的那样,不需要显式子流程。也可以做一个

echo a | if true
then
  cat
fi

Print 函数不读取标准输入,因此没有必要将数据传输到它。使用 Print 的当前实现来做你想做的事情的一种可能方法是:

if ! occ_output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1); then
    Print "Error: Failed to scan files. Are you in maintenance mode?"
fi

Print "'occ' output: $occ_output"

由于 if 语句的正文中只有一行,您可以使用 || 代替:

occ_output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1) \
    || Print "Error: Failed to scan files. Are you in maintenance mode?"

Print "'occ' output: $occ_output"

2>&1 导致 occ 的标准输出和错误输出都被捕获到 occ_output

请注意,Print 函数的主体可以简化为:

[[ $quiet_mode == No ]] && printf '%s\n' ""
(( logging ))           && printf '%s\n' "" >> "$log_file"

请参阅对 Why is printf better than echo? 的公认且优秀的回答,以了解我将 echo "" 替换为 printf '%s\n' "" 的原因的解释。