gpt4 book ai didi

linux - Bash 一种在出现管道错误时保留原始管道输出文件的方法?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:08:53 25 4
gpt4 key购买 nike

有很多方法可以使用中间文件和函数或追加/截断来执行此操作,但我很好奇是否有人知道如何用一种单行方式来表达有条件的非破坏性覆盖,例如:

# 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,但如果没有 输出流,则不会。

我不知道有什么 bash-wise 可以解释这一点,因为我相当确定它不会挂起与管道 stdout 文件相关的基本文件设置,直到它看到输出流。除非有一个神奇的 set -o lazy 选项潜伏着。

因此,我正在寻找一种单行方式,使管道比急切的 wrt 基本 stdout 文件设置更懒惰,使用 bash 约定“在第一次实际输出时”触发。

最佳答案

给 future 读者的快速说明

此处的原始问题仅询问在修改输出文件之前等待至少一行输出通过管道。这与确保管道在覆盖目标文件之前以非零状态退出是不同的,但是添加了“取决于管道成功”部分,其中仅包含有关该方法的详细信息。


取决于流水线的成功

不幸的是,这个不像单行代码那么容易写。

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 之前。

关于linux - Bash 一种在出现管道错误时保留原始管道输出文件的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41113233/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com