考虑这个脚本:
tmpfile=$(mktemp)
cat <<EOS > "$tmpfile"
line 1
line 2
line 3
EOS
cat <(tail -1 "$tmpfile") "$tmpfile"
Run Code Online (Sandbox Code Playgroud)
这有效并输出:
line 3
line 1
line 2
line 3
Run Code Online (Sandbox Code Playgroud)
假设我们的输入源不是实际文件,而是标准输入:
cat <<EOS | # what goes here now?
line 1
line 2
line 3
EOS
Run Code Online (Sandbox Code Playgroud)
我们如何修改命令:
cat <(tail -1 "$tmpfile") "$tmpfile"
Run Code Online (Sandbox Code Playgroud)
这样它在不同的上下文中仍然产生相同的输出?
注意:我正在寻找的特定 Heredoc 以及 Heredoc 本身的使用仅是说明性的。任何可接受的答案都应该假设它通过 stdin 接收任意数据。
尝试:
awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'
Run Code Online (Sandbox Code Playgroud)
用我们的输入定义一个变量:
$ input="line 1
> line 2
> line 3"
Run Code Online (Sandbox Code Playgroud)
运行我们的命令:
$ echo "$input" | awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'
line 3
line 1
line 2
line 3
Run Code Online (Sandbox Code Playgroud)
或者,当然,我们可以使用 here-doc:
$ cat <<EOS | awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'
line 1
line 2
line 3
EOS
line 3
line 1
line 2
line 3
Run Code Online (Sandbox Code Playgroud)
x=x $0 ORS
这会将每一行输入附加到变量x
。
在 awk 中,ORS
是输出记录分隔符。默认情况下,它是一个换行符。
END{printf "%s", $0 ORS x}
在我们读入整个文件后,这将打印最后一行$0
,然后是整个文件的内容x
。
由于这会将整个输入读取到内存中,因此不适用于大型(例如千兆字节)输入。
如果 stdin 指向一个可查找的文件(比如 bash 的(但不是所有其他 shell 的)here 文档,这些文档是用临时文件实现的),您可以获取尾部,然后在阅读完整内容之前返回:
zsh
或ksh93
shell 或脚本语言(如 tcl/perl/python)中可以使用搜索运算符,但在bash
. 但是,bash
如果您必须使用bash
.
ksh93 -c 'tail -n1; cat <#((0))' <<...
Run Code Online (Sandbox Code Playgroud)
或者
zsh -c 'zmodload zsh/system; tail -n1; sysseek 0; cat' <<...
Run Code Online (Sandbox Code Playgroud)
现在,当 stdin 指向诸如管道或套接字之类的不可查找文件时,这将不起作用。然后,唯一的选择是读取和存储(在内存或临时文件中...)整个输入。
已经给出了一些存储在内存中的解决方案。
使用临时文件,使用zsh
,您可以使用:
seq 10 | zsh -c '{ cat =(sed \$w/dev/fd/3); } 3>&1'
Run Code Online (Sandbox Code Playgroud)
如果在 Linux 上,使用bash
或zsh
或任何将临时文件用于 here-document 的 shell,您实际上可以使用由 here-document 创建的临时文件来存储输出:
seq 10 | {
chmod u+w /dev/fd/3 # only needed in bash5+
cat > /dev/fd/3
tail -n1 /dev/fd/3
cat <&3
} 3<<EOF
EOF
Run Code Online (Sandbox Code Playgroud)