在 bash 中回显到 stdout 或文件的优雅解决方案

Bre*_*lad 4 bash shell-script stdout

我有一个 bash 应用程序正在产生一些结果,我想将结果回显stdout到用户选择的文件中。因为我还向屏幕回显了其他交互式消息,所以要求用户>在他想将结果回显到文件时明确使用重定向不是一个选项 (*),因为这些消息也会出现在文件中。

现在我有一个解决方案,但它很难看。

if [ -z $outfile ]
then
    echo "$outbuf"    # Write output buffer to the screen (stdout)
else
    echo "$outbuf" > $outfile  # Write output buffer to file
fi
Run Code Online (Sandbox Code Playgroud)

我试图让变量$outfile等于stdout, to&1或者其他东西,但它只会写入具有该名称的文件,而不是实际写入标准输出。有没有更优雅的解决方案?

(*) 我可以作弊并stderr为此目的使用,但我认为它也很丑陋,不是吗?

Sté*_*las 8

首先,您应该避免echo输出任意数据

在基于 Linux 的系统以外的系统上,您可以使用:

logfile=/dev/stdout
Run Code Online (Sandbox Code Playgroud)

对于 Linux,这适用于某些类型的 stdout,但是当 stdout 是套接字时会失败,或者更糟的是,如果 stdout 是常规文件,则会截断该文件,而不是在 stdout 在文件中的当前位置写入。

除此之外,在类似 Bourne 的 shell 中,无法进行条件重定向,但您可以使用eval

eval 'printf "%s\n" "$buf" '${logfile:+'> "$logfile"'}
Run Code Online (Sandbox Code Playgroud)

您可以使用专用文件描述符代替变量

exec 3>&1
[ -z "$logfile" ] || exec 3> "$logfile"

 printf '%s\n' "$buf" >&3
Run Code Online (Sandbox Code Playgroud)

这样做的一个(小)缺点是,除了 in 之外ksh,fd 3 会泄露给脚本中运行的每个命令。使用zsh,您可以sysopen -wu 3 -o cloexec -- "$logfile" || exit代替exec 3> "$logfile"bash没有等效项。

另一个常见的习惯用法是使用如下函数:

log() {
  if [ -n "$logfile" ]; then
    printf '%s\n' "$@" >> "$logfile"
  else
    printf '%s\n' "$@"
  fi
}

log "$buf"
Run Code Online (Sandbox Code Playgroud)


Kus*_*nda 5

  1. 设置outfile"/dev/stdout"
  2. 让用户选择文件名、覆盖outfile或保留默认值。
  3. printf '%s\n' "$outbuf" >"$outfile"

我使用printf是因为“为什么 printf 比 echo 好? ”。

有关此解决方案的注意事项,请参阅Stéphane Chazelas 的回答