来自“的一个小扩展问题cat 第 x 行到大文件中的第 y 行”:
我有一个巨大的文件(2-3 GB)。我只想从具有“foo:”的行到具有“goo:”的行进行cat/打印。假设“foo:”和“goo:”在一个文件中只出现一次; “foo:”继续“goo:”。
到目前为止,这是我的方法:
- 首先,找到包含“foo:”和“goo:”的行:
grep -nr "foo:" bigfile
- 退货
123456: foo: hello world!
和654321: goo: good bye!
- 一旦我知道这些起始和结束行号以及差异(654321-123456=530865),我就可以进行选择性猫:
tail -n+123456 bigfile | head -n 530865
我的问题是如何有效地用表达式(例如 grep ...)替换行号常量?
我可以编写一个简单的 Python 脚本,但希望仅使用组合命令来实现它。
答案1
sed -n '/foo/,/goo/p;/goo/q' <bigfile
那只会打印那些行。如果您想要行号,您可以添加一个=
.
sed -n '/foo/=;/goo/=;//q' <bigfile
它q
很重要,因为它q
在调用时适合输入 - 否则sed
将继续读取 infile 直到最后。
如果你不想打印foo/goo
行,你可以这样做:
使用 GNU sed
:
sed -n '/foo/,/goo/!d;//!p;/goo/q
' <<\DATA
line1
foo
line3
line4
line5
goo
line7
DATA
输出
line3
line4
line5
以及任何其他:
sed -n '/foo/G;/\n/,/goo/!d;//q;/\n/!p
' <<\DATA
line1
foo
line3
line4
line5
goo
line7
DATA
输出
line3
line4
line5
但无论哪种方式,一旦遇到搜索中的最后一行,它也会退出输入。
答案2
如果您同意放弃当前在子 shell 中使用某些内容来获取行号并允许另一个实用程序打印文件的方法,那么这可以awk
轻松地以 pure 方式完成:
如果你想打印这些行之间 foo:
而goo:
不是行本身,那么您可以使用以下(最初是从这里拾取的):
awk '/goo:/ { exit }; flag; /foo:/ { flag = 1 }' bigFile
上面的exit
s 当它看到结束标记 ( goo:
) 时,print
如果 sflag
为 true,则当它到达开始标记 ( ) 时设置flag
为 true(实际上) 。1
foo:
但是,如果您希望在输出中包含令牌行,则该命令实际上更简单,如下所示@jasonwryan 提到:
awk '/foo:/,/goo:/' bigFile
如果您一心只想获取行号,而不是使用相同的实用程序实际打印文件,那么您可以获取开始和结束标记的行号,如下所示:
awk '/foo:|goo:/ { print NR }' bigFile
答案3
替代方案sed
一:
sed '/foo/,$!d;/goo/q'
答案4
要将常量替换为表达式,您可以使用命令替换。
要将命令的输出替换为表达式,请使用$(command)
在这种情况下,适当的命令行是:
tail -n+$(grep -nr "foo:" bigfile | cut -d':' -f1) bigfile | \
head -n$(($(grep -nr "goo:" bigfile | cut -d':' -f1)-$(grep -nr "foo:" bigfile | cut -d':' -f1)+1))
这将打印从包含 的行foo:
到包含 的行goo:
(包含)的所有行。