我有一个 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
我试图让变量$outfile
等于stdout
、 to &1
,也许还有其他东西,但它只会写入具有该名称的文件,而不是实际写入标准输出。有更优雅的解决方案吗?
(*) 我可以作弊并用于stderr
此目的,但我认为这也很丑陋,不是吗?
答案1
首先,你应该避免echo
输出任意数据。
在基于 Linux 的系统以外的系统上,您可以使用:
logfile=/dev/stdout
对于 Linux,这适用于某些类型的 stdout,但当 stdout 是套接字时会失败,或者更糟糕的是,如果 stdout 是常规文件,则会截断该文件,而不是在 stdout 在文件中的当前位置写入。
除此之外,在类似 Bourne 的 shell 中,没有办法有条件的重定向,尽管您可以使用eval
:
eval 'printf "%s\n" "$buf" '${logfile:+'> "$logfile"'}
代替多变的,你可以使用专用的文件描述符:
exec 3>&1
[ -z "$logfile" ] || exec 3> "$logfile"
printf '%s\n' "$buf" >&3
这样做的一个(小)缺点是,除了 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"
答案2
- 设置。
outfile
"/dev/stdout"
- 让用户选择文件名、覆盖
outfile
或保留默认值。 printf '%s\n' "$outbuf" >"$outfile"
我使用是printf
因为“为什么 printf 比 echo 更好?”。
有关此解决方案的注意事项,请参阅斯特凡·查泽拉斯的回答。
答案3
尝试这个
#!/bin/bash
TOSCREEN="1" # empty OR 0 to log to file
if [[ ! -z "$TOSCREEN" && $TOSCREEN == "1" ]]; then
echo "$outbuf" # Write output buffer to the screen (stdout)
else
if [[ ! -f $outfile ]]; then
#echo -e "Making file "
touch "$outfile"
fi
echo "$outbuf" >> $outfile # Write output buffer to file
fi
答案4
#!/bin/bash -ue
function stdout_or_file()
{
DUMP_FILE=${1:-}
if [ -z "${DUMP_FILE}" ]; then
# print stdin to stdout
cat
else
# dump stdin to a file
sed -n "w ${DUMP_FILE}"
fi
}
echo "foo" | stdout_or_file "${outfile}"
请注意,这${1:-}
是特定于 bash 的,并且在“-u”模式下特别有用。甚至可以在其他 shell 中工作的通用单行可能是
echo "foo" | if [ -z "${outfile}" ]; then cat; else sed -n "w ${outfile}"; fi