Answers:
除非您的网段确实很大(例如:您实际上不能保留那么多的RAM,大概是因为这是一个控制大型文件系统的微型嵌入式系统),否则,单遍确实是更好的方法。不仅因为它会更快,而且最重要的是因为它使源成为一个流,从中读取或保存的所有数据都将丢失。尽管sed也可以做到,但这确实是awk的工作。
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)
如果必须使用两次通过方法,请确定最后一个分隔符的行偏移量并从中进行打印。或者确定字节偏移量并从中打印。
</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)
')
附录:如果您拥有的不仅仅是POSIX,这是一个简单的单遍版本,它依赖于awk的通用扩展名,该扩展名允许记录分隔符RS
为正则表达式(POSIX仅允许单个字符)。这不是完全正确的:如果文件以记录分隔符结尾,它将在最后一个记录分隔符之前打印块,而不是空记录。第二个版本使用RT
避免了该缺陷,但是RT
特定于GNU awk。
awk -vRS='(^|\n)---+($|\n)' 'END{printf $0}'
gawk -vRS='(^|\n)---+($|\n)' 'END{if (RT == "") printf $0}'
cut
示例中的复制粘贴后编辑。我认为该awk
示例没有错,您使用的是什么版本的awk,您的测试输入是什么?
awk
版本正在运行..它在一个大文件上占用了很长时间。.该sed
版本在0.470s内处理了同一文件..我的测试数据非常加权...只有两个块,其中一个'-'从一百万行的末尾开始三行...
两次通过策略似乎是正确的选择。我会使用sed而不是sed awk(1)
。两遍可能看起来像这样:
$ LINE=`awk '/^---$/{n=NR}END{print n}' file`
获取行号。然后回显从该行号开始的所有文本:
$ awk "NR>$LINE" file
这应该不需要过多的缓冲。
awk -v line=$(awk '/^---$/{n=NR}END{print n}' file) 'NR>line' file
lnum=$(($(sed -n '/^---$/=' file | sed '$!d') +1)); sed -n "${lnum},$ p" file
第一个sed
输出“ ---”行的行号...
第二个sed
从第一个sed的输出中提取最后一个数字...
在该数字上加1以获取“ ccc”块的开始...
第三个从“ ccc”块的开始到EOF的“ sed”输出
更新 (使用Gilles方法的改进信息)
好吧,我想知道格伦·杰克曼的 tac
表现如何,所以我对这三个答案进行了时间测试(在撰写本文时)……测试文件每个都包含一百万行(各自的行号)。
所有的答案都达到了预期的效果...
这是时间。
吉尔斯 sed
(单次通过)
# real 0m0.470s
# user 0m0.448s
# sys 0m0.020s
吉尔斯 awk
(单次通过)
# very slow, but my data had a very large data block which awk needed to cache.
吉尔斯 “两次通过”(第一种方法)
# real 0m0.048s
# user 0m0.052s
# sys 0m0.008s
吉尔斯 “两次通过”(第二种方法)... 非常快
# real 0m0.204s
# user 0m0.196s
# sys 0m0.008s
吉尔斯 “两次通过”(第三种方法)
# real 0m0.774s
# user 0m0.688s
# sys 0m0.012s
吉尔 '徒劳无功(RT法)...... 非常快,但不是POSIX。
# real 0m0.221s
# user 0m0.200s
# sys 0m0.020s
glenn jackman ... 非常快,但不是POSIX。
# real 0m0.022s
# user 0m0.000s
# sys 0m0.036s
熊
# real 0m0.464s
# user 0m0.432s
# sys 0m0.052s
麦基·梅塞尔(Mackie Messer)
# real 0m0.856s
# user 0m0.832s
# sys 0m0.028s
使用“ tac ”从头到尾输出文件行:
tac afile | awk '/---/ {exit} {print}' | tac
tac
不是POSIX,而是特定于Linux的(在GNU coreutils和某些busybox安装中)。
你可以用 ed
ed -s infile <<\IN
.t.
1,?===?d
$d
,p
q
IN
工作原理:t
复制当前(.
)行-在ed
开始时始终是最后一行(以防最后一行出现定界符),1,?===?d
删除直到并包括前一个匹配项的所有行(ed
仍在最后一行) ),然后$d
删除(重复的)最后一行,,p
打印文本缓冲区(替换w
为在适当位置编辑文件),最后q
退出ed
。
如果您知道输入中至少有一个定界符(并且不在乎是否也已打印出来),则
sed 'H;/===/h;$!d;x' infile
会更短。
工作原理:将所有行添加到H
旧缓冲区,h
遇到匹配项时覆盖旧缓冲区,当更改缓冲区(和自动打印)时,将d
删除除最后一行以外的所有行。 $
x
sed
工作正常,但是我无法awk
运行示例;它挂起...我得到第三个例子一个错误:cut -f ':' -t 1
...斩:无效选项- “T”