Bash 重用进程替换文件

dog*_*ane 6 bash io-redirection process-substitution

我有一个大脚本,它将文件作为输入并用它做各种事情。这是一个测试版本:

echo "cat: $1"
cat $1
echo "grep: $1"
grep hello $1
echo "sed: $1"
sed 's/hello/world/g' $1
Run Code Online (Sandbox Code Playgroud)

我希望我的脚本可以使用进程替换,但只有第一个命令 ( cat) 有效,而其余的则无效。我认为这是因为它是一个管道。

$ myscript.sh <(echo hello)
Run Code Online (Sandbox Code Playgroud)

应该打印:

cat: /dev/fd/63
hello
grep: /dev/fd/63
hello
sed: /dev/fd/63
world
Run Code Online (Sandbox Code Playgroud)

这可能吗?

Gil*_*il' 8

<(…)构造创建了一个管道。管道通过文件名传递,如/dev/fd/63,但这是一种特殊类型的文件:打开它实际上意味着复制文件描述符 63。(有关更多解释,请参阅此答案的末尾。)

从管道中读取是一种破坏性操作:一旦捕获到一​​个字节,就无法将其扔回去。所以你的脚本需要保存管道的输出。您可以使用临时文件(如果输入很大,则最好)或变量(如果输入很小,则最好)。使用临时文件:

tmp=$(mktemp)
cat <"$1" >"$tmp"
cat <"$tmp"
grep hello <"$tmp"
sed 's/hello/world/g' <"$tmp"
rm -f "$tmp"
Run Code Online (Sandbox Code Playgroud)

(您可以将两个调用组合为catas tee <"$1" -- "$tmp"。)使用变量:

tmp=$(cat)
printf "%s\n"
printf "%s\n" "$tmp" | grep hello
printf "%s\n" "$tmp" | sed 's/hello/world/g'
Run Code Online (Sandbox Code Playgroud)

请注意,命令替换会$(…)截断命令输出末尾的所有换行符。为避免这种情况,请添加一个额外的字符,然后将其删除。

tmp=$(cat; echo a); tmp=${tmp%a}
printf "%s\n"
printf "%s\n" "$tmp" | grep hello
printf "%s\n" "$tmp" | sed 's/hello/world/g'
Run Code Online (Sandbox Code Playgroud)

顺便说一句,不要忘记变量替换周围的双引号。