我有一个带有标记行的文本,例如:
aaa
---
bbb
---
ccc
Run Code Online (Sandbox Code Playgroud)
我需要从最后一个标记(不包括)到 EOF 获取文本。在这种情况下,它将是
ccc
Run Code Online (Sandbox Code Playgroud)
在 POSIX.2 中有一种优雅的方式吗?现在我使用两次运行:第一次使用nl
和grep
最后一次使用各自的行号。然后我提取行号并用于sed
提取有问题的块。
文本段可能非常大,所以我害怕使用一些文本添加方法,比如我们将文本添加到缓冲区,如果我们遇到标记,我们清空缓冲区,这样在 EOF 时我们就有了最后一个块缓冲。
除非你的段真的很大(比如:你真的不能节省那么多内存,大概是因为这是一个控制大型文件系统的小型嵌入式系统),单次传递确实是更好的方法。不仅因为它会更快,而且最重要的是因为它允许源是一个流,从中读取和未保存的任何数据都将丢失。这确实是 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)
两次通过策略似乎是正确的。我会使用 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)
这不应该需要过多的缓冲。