将标准输入的最后一行添加到整个标准输入中

Jon*_*nah 9 bash

考虑这个脚本:

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 接收任意数据

Joh*_*024 7

尝试:

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

由于这会将整个输入读取到内存中,因此不适用于大型(例如千兆字节)输入。


Sté*_*las 5

如果 stdin 指向一个可查找的文件(比如 bash 的(但不是所有其他 shell 的)here 文档,这些文档是用临时文件实现的),您可以获取尾部,然后在阅读完整内容之前返回:

zshksh93shell 或脚本语言(如 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 上,使用bashzsh或任何将临时文件用于 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)