在 POSIX.2 中从最后一个标记获取文本到 EOF

aik*_*ooh 9 text-processing

我有一个带有标记行的文本,例如:

aaa
---
bbb
---
ccc
Run Code Online (Sandbox Code Playgroud)

我需要从最后一个标记(不包括)到 EOF 获取文本。在这种情况下,它将是

ccc
Run Code Online (Sandbox Code Playgroud)

在 POSIX.2 中有一种优雅的方式吗?现在我使用两次运行:第一次使用nlgrep最后一次使用各自的行号。然后我提取行号并用于sed提取有问题的块。

文本段可能非常大,所以我害怕使用一些文本添加方法,比如我们将文本添加到缓冲区,如果我们遇到标记,我们清空缓冲区,这样在 EOF 时我们就有了最后一个块缓冲。

Gil*_*il' 6

除非你的段真的很大(比如:你真的不能节省那么多内存,大概是因为这是一个控制大型文件系统的小型嵌入式系统),单次传递确实是更好的方法。不仅因为它会更快,而且最重要的是因为它允许源是一个流,从中读取和未保存的任何数据都将丢失。这确实是 awk 的工作,尽管 sed 也可以做到。

sed -n -e 's/^---$//' -e 't a' \
       -e 'H' -e '$g' -e '$s/^\n//' -e '$p' -e 'b' \
       -e ':a' -e 'h'              # you are not expected to understand this
awk '{if (/^---$/) {chunk=""}      # separator ==> start new chunk
      else {chunk=chunk $0 RS}}    # append line to chunk
     END {printf "%s", chunk}'     # print last chunk (without adding a newline)
Run Code Online (Sandbox Code Playgroud)

如果您必须使用两遍方法,请确定最后一个分隔符的行偏移并从中打印。或者确定字节偏移量并从中打印。

</input/file tail -n +$((1 + $(</input/file         # print the last N lines, where N=…
                               grep -n -e '---' |   # list separator line numbers
                               tail -n 1 |          # take the last one
                               cut -d ':' -f 1) ))  # retain only line number
</input/file tail -n +$(</input/file awk '/^---$/ {n=NR+1} END {print n}')
</input/file tail -c +$(</input/file LC_CTYPE=C awk '
    {pos+=length($0 RS)}        # pos contains the current byte offset in the file
    /^---$/ {last=pos}          # last contains the byte offset after the last separator
    END {print last+1}          # print characters from last (+1 because tail counts from 1)
')
Run Code Online (Sandbox Code Playgroud)

附录:如果您有多个 POSIX,这里有一个简单的一次性版本,它依赖于 awk 的通用扩展,它允许记录分隔符RS是一个正则表达式(POSIX 只允许一个字符)。这并不完全正确:如果文件以记录分隔符结尾,它会在最后一个记录分隔符之前打印块而不是空记录。使用的第二个版本RT避免了该缺陷,但RT特定于 GNU awk。

awk -vRS='(^|\n)---+($|\n)' 'END{printf $0}'
gawk -vRS='(^|\n)---+($|\n)' 'END{if (RT == "") printf $0}'
Run Code Online (Sandbox Code Playgroud)


Mac*_*ser 3

两次通过策略似乎是正确的。我会使用 sed 而不是awk(1). 两个通道可能如下所示:

$ LINE=`awk '/^---$/{n=NR}END{print n}' file`
Run Code Online (Sandbox Code Playgroud)

获取行号。然后回显从该行号开始的所有文本:

$ awk "NR>$LINE" file
Run Code Online (Sandbox Code Playgroud)

这不应该需要过多的缓冲。