使用中间文件和函数或追加/截断有很多方法可以做到这一点,但我很好奇是否有人知道如何表达条件非破坏性覆盖的单行方式如下:
# outfile not touched if script1 fails
script1 > outfile
# outfile not touched if script1 or script2 fails
script1 | script2 > outfile
# or maybe a "lazy >" command, but I'd rather see how bash itself can do this
script1 | script2 | magicproxy outfile
其中outfile的先前内容保持不变如果其管道无法输出任何有效的流内容。
如果某些输出流,可以搞砸outfile,但如果没有输出流则不行。
我不知道有什么打击方式来解释这一点,因为我相当肯定它不会在与管道标准输出文件有关的基本文件设置之前看到输出流。除非有潜伏的魔法套装 - 懒惰选项。
因此,我正在寻找一种单行方式,使管道比基本的stdout文件设置更加懒惰,在第一次实际输出"触发",使用bash约定。
答案 0 :(得分:2)
这里的原始问题仅询问在修改输出文件之前是否等待至少一行输出通过管道。这与不一样确保管道在覆盖目标文件之前以非零状态退出,但是部分"取决于管道成功"已添加有关该方法的详细信息。
不幸的是,这并不像单行一样容易写。
shopt -s pipefail # fail if any component of a pipeline doesn't succeed
tempfile=$(mktemp "$1".XXXXXX)
if script1 | script2 >"$tempfile"; then
mv -- "$tempfile" "$1"
else
rm -f -- "$tempfile"
fi
一种显而易见的方法是使用eval
,但这需要非常小心以避免安全漏洞:
# DANGER: command_source *must* be a constant string; MUST NOT substitute argument values
# directly into source -- instead, refer to variables from that string.
eval_to_output_file() {
local command_source destfile tempfile retval
command_source=$1
destfile=$2
tempfile=$(mktemp -- "$destfile.XXXXXX")
if eval "$command_source" >"$tempfile"; then
mv -- "$tempfile" "$destfile"
else
retval=$?
rm -f -- "$tempfile"
return "$retval"
fi
}
...用法:
# CRITICAL that script argument is single-quoted, and all arguments expanded by eval
# ...otherwise, expansions can perform code injection.
eval_to_output_file 'script1 "$arg1" | script2 "$arg2"' outfile
更安全的是,管道可以封装在一个函数中:
# Safer alternative to eval_to_output_file
# Requires that a pipeline be encapsulated into a function
run_to_output_file() {
local destfile retval
destfile=$1; shift
tempfile=$(mktemp -- "$destfile.XXXXXX")
if "$@" >"$tempfile"; then
mv -- "$tempfile" "$destfile"
else
retval=$?
rm -f -- "$tempfile"
return "$retval"
fi
}
# example of a function running your pipeline
# note that arguments are passed through by the above
myfunc() ( # myfunc() ( ) instead of myfunc() { } means execute in a subshell
shopt -s pipefail # because we're in a subshell, this won't propagate out
script1 "$1" | script2 "$2"
)
run_to_output_file outfile myfunc arg1 arg2
magicproxy() {
[[ $1 ]] || { echo "Usage: magicproxy filename" >&2; return 1; }
if IFS= read -r first_line; then
cat <(printf '%s\n' "$first_line") - >"$1"
fi
}
请注意,这意味着您的输出文件在某些时候将存在部分内容。
magicproxy() {
[[ $1 ]] || { echo "Usage: magicproxy filename" >&2; return 1; }
local tempfile
tempfile=$(mktemp -- "$1.XXXXXX") || return
if cat >"$tempfile" && [[ -s "$tempfile" ]]; then
mv -- "$tempfile" "$1"
else
rm -f -- "$tempfile"
fi
}
...将适用于您提议的管道:
script1 | script2 | magicproxy outfile
也就是说,如果您使用的是GNU系统并且mktemp
使用的限制权限不适合您,您可能还想添加:
# give your temporary file the same permissions as your destination
chmod --reference="$1" -- "$tempfile"
... mv
之前。