【问题标题】:Conditionally directing a command's output to /dev/null in bash [duplicate]有条件地将命令的输出定向到 bash 中的 /dev/null [重复]
【发布时间】:2018-05-13 03:21:03
【问题描述】:

我有以下 bash 脚本:

flag=false
command_name \
$(  flag == false  && printf %s '>/dev/null') 

我希望终端没有输出,但我仍然得到一些。如果我将输出重定向到 /dev/nullcommand-name 的同一行而不进行扩展,那么它将被抑制。

命令是来自 android SDK 的 dx 工具

编辑 1: 这里是脚本中的代码

dx \
    --dex \
    $( ( (( flag_v == 1 )) || (( flag_v == 'd' ))) && printf %s '--verbose') \
    --no-strict \
    --output="../"$app_name.jar \
    $(find . -type f -name '*.class') \
    $( $dexflag == false && printf %s '>/dev/null')

当我运行该工具时,它按预期工作。我不认为这可能是错误流。

【问题讨论】:

  • 通过shellcheck.net 运行您的代码(使用#!/bin/bash 作为第一行),然后如果您仍有问题,请使用没有语法问题的代码更新您的Q。另外,你知道std-error 的概念吗?您还需要将该流重定向到/dev/null。试试... > /dev/null 2>&1(我没有否决你的Q)。祝你好运。
  • 扩展的结果,例如$( ),不会被解析为代码。因此,如果这些结果本身包含扩展,它们将不会被兑现。这是一项功能,而不是错误——否则编写处理不受信任数据的 shell 脚本将是一场噩梦。
  • ...上面假设$dexflag == false 是一个有效的陈述,但事实并非如此。 (如果dexflag 为空,它将运行== false 作为命令;如果是true,它将运行true == false 作为命令;由于true 命令忽略您的参数,结果将始终是true;如果$dexflag 包含false,则false == false 将始终返回false,因为false 命令同样根本不查看其参数)。
  • (( ))算术上下文;你不能在那里进行字符串比较(当它被允许时,它会将字符串评估为它们命名的变量的数值)。
  • (见标记的骗子,顺便说一句——这是一个很好的例子,说明问题应该是什么样子的,用最简单的代码来展示问题,无需任何工具即可运行/setup 需要)。

标签: bash terminal output-redirect


【解决方案1】:

有条件地重定向标准输出

重定向是 shell 语法——它们必须在参数扩展之前的解析阶段被识别,因此您不能通过变量扩展生成它们(不提交 evil)。

可以做的事情(在 bash 4.1 或更高版本中)是有一个无条件重定向,但是 它重定向到的东西改变了:

# Create an out_fd variable that points to stdout (FD 1) if dexflag != "false", or to a new
# handle on /dev/null otherwise
if [[ $dexflag = false ]]; then
  exec {out_fd}>/dev/null # maybe put 2>&1 as well to suppress stderr
else
  out_fd=1 # use FD 1 (stdout)
fi

# run dex with its stdout redirected to the FD number in "out_fd"
dex ... >&"$out_fd"

# if out_fd is not stdin/stdout/stderr, then go ahead and close it when done.
(( out_fd > 2 )) && exec {out_fd}>&-

注意:

  • 字符串比较以[[ $var = $pattern ]] 的形式进行(或[[ $var = "$string" ]] 进行精确匹配)。见the bash-hackers' wiki on the conditional expression
  • 在 bash 4.1 或更高版本中,exec {fd_varname}>file 打开file,并将指向该文件的文件描述符编号放入变量fd_varnameexec {fd_varname}>&- 关闭编号存储在fd_varname 中的文件描述符。
  • 使用旧版本的 bash,您仍然可以执行此逻辑,但您需要手动分配文件描述符编号,而不是自动分配文件描述符编号,手动分配其他未使用的 FD 编号0、1 或 2(为标准输入、标准输出和标准错误保留)。因此,在这种情况下,可能是if 分支中的exec 3>/dev/nullexec 3>&1dex 命令上的>&3,以及关闭它的exec 3>&-

有条件地安全地生成参数列表

请参阅BashFAQ #50 进行长时间讨论。简而言之:除了重定向到 /dev/null 之外的所有内容,需要进行一个简单的更改以使其符合最佳实践:使用数组。

#!/bin/bash

args=( )

case $flag_v in
  1|d) args+=( --verbose ) ;;
esac

while IFS= read -r -d '' filename; do
  args+=( "$filename" )
done < <(find . -type f -name '*.class' -print0)

dx --dex --no-strict --output="../$app_name.jar" "${args[@]}"
  • 请参阅 BashPitfalls #1 描述为什么 $(find ...)(如 $(ls ...))不安全,并参阅 Using Find 了解最佳实践。
  • 请参阅BashFAQ #24,了解为什么使用while read ...; do ...; done &lt; &lt;(find ...) 而不是find ... | while read ...; do ...; done

【讨论】:

  • 标记社区 wiki 以避免从已知的欺骗中获取代表。
猜你喜欢
  • 2023-04-08
  • 2019-10-11
  • 1970-01-01
  • 2012-01-15
  • 2017-06-03
  • 2020-01-21
  • 2021-02-28
  • 1970-01-01
  • 2023-03-06
相关资源
最近更新 更多