我有一个约 23000 行的 SQL 转储,其中包含几个数据库的数据。我需要提取该文件的某个部分(即单个数据库的数据)并将其放在一个新文件中。我知道我想要的数据的开始行号和结束行号。
有谁知道一个 Unix 命令(或一系列命令)从一个文件中提取所有行,比如 16224 和 16482 行,然后将它们重定向到一个新文件中?
我有一个约 23000 行的 SQL 转储,其中包含几个数据库的数据。我需要提取该文件的某个部分(即单个数据库的数据)并将其放在一个新文件中。我知道我想要的数据的开始行号和结束行号。
有谁知道一个 Unix 命令(或一系列命令)从一个文件中提取所有行,比如 16224 和 16482 行,然后将它们重定向到一个新文件中?
sed -n '16224,16482p;16483q' filename > newfile
从sed 手册:
p - 打印出模式空间(到标准输出)。此命令通常仅与 -n 命令行选项一起使用。
n - 如果未禁用自动打印,则打印模式空间,然后无论如何用下一行输入替换模式空间。如果没有更多输入,则 sed 退出,不再处理任何命令。
q - 退出
sed
而不处理任何更多的命令或输入。请注意,如果未使用 -n 选项禁用自动打印,则会打印当前模式空间。
sed 脚本中的地址可以是以下任何一种形式:
number 指定行号将仅匹配输入中的该行。
可以通过指定用逗号 (,) 分隔的两个地址来指定地址范围。地址范围匹配从第一个地址匹配的位置开始的行,并一直持续到第二个地址匹配(包括)。
sed -n '16224,16482 p' orig-data-file > new-file
其中 16224,16482 是起始行号和结束行号,包括。这是 1 索引的。 -n
抑制将输入作为输出回显,这显然是您不想要的;数字表示执行以下命令的行范围;该命令p
打印出相关行。
使用头/尾非常简单:
head -16482 in.sql | tail -258 > out.sql
使用 sed:
sed -n '16224,16482p' in.sql > out.sql
使用 awk:
awk 'NR>=16224&&NR<=16482' in.sql > out.sql
您可以使用“vi”,然后使用以下命令:
:16224,16482w!/tmp/some-file
或者:
cat file | head -n 16482 | tail -n 258
编辑:- 只是为了添加解释,您使用head -n 16482显示前 16482 行,然后使用tail -n 258从第一个输出中获取最后 258 行。
还有另一种方法awk
:
awk 'NR==16224, NR==16482' file
如果文件很大,最好exit
在阅读最后一行之后。这样,它就不会不必要地读取以下行:
awk 'NR==16224, NR==16482-1; NR==16482 {print; exit}' file
awk 'NR==16224, NR==16482; NR==16482 {exit}' file
perl -ne 'print if 16224..16482' file.txt > new_file.txt
站在boxxar的肩膀上,我喜欢这样:
sed -n '<first line>,$p;<last line>q' input
例如
sed -n '16224,$p;16482q' input
意思是“$
最后一行”,所以第一个命令sed
打印所有以 line 开头的行16224
,第二个命令在打印 line之后sed
退出。(在 boxxar 的解决方案中添加-range似乎没有必要。)16428
1
q
我喜欢这个变体,因为我不需要两次指定结束行号。而且我测量到使用$
不会对性能产生不利影响。
# print section of file based on line numbers
sed -n '16224 ,16482p' # method 1
sed '16224,16482!d' # method 2
人们试图围绕计算head | tail
组合的间隔而思考过度。
以下是在不计算任何内容的情况下获得“16224 -- 16482”范围的方法:
cat file | head -n +16482 | tail -n +16224
解释:
+
指示head
/tail
命令“上升到/开始于”(分别)从文件开头算起的指定行号。
类似地,a指示-
他们“从文件末尾开始计数”(分别)指定的行号
上面显示的解决方案首先简单地使用head
“将所有内容保持在顶部数字”,然后tail
使用第二个,“将所有内容从底部数字向上保持”,从而定义了我们感兴趣的范围(无需计算间隔)。
cat dump.txt | head -16224 | tail -258
应该做的伎俩。这种方法的缺点是您需要进行算术运算来确定 tail 的参数并说明您是否希望 'between' 包含结束行。
sed -n '16224,16482p' < dump.sql
又快又脏:
head -16428 < file.in | tail -259 > file.out
可能不是最好的方法,但它应该工作。
顺便说一句:259 = 16482-16224+1。
我编写了一个名为splitter的 Haskell 程序,它正是这样做的:阅读我的发布博客文章。
您可以按如下方式使用该程序:
$ cat somefile | splitter 16224-16482
这就是它的全部。您将需要 Haskell 来安装它。只是:
$ cabal install splitter
你完成了。我希望你发现这个程序很有用。
甚至我们可以这样做在命令行检查:
cat filename|sed 'n1,n2!d' > abc.txt
例如:
cat foo.pl|sed '100,200!d' > abc.txt
使用红宝石:
ruby -ne 'puts "#{$.}: #{$_}" if $. >= 32613500 && $. <= 32614500' < GND.rdf > GND.extract.rdf
我想从使用变量的脚本中做同样的事情,并通过在 $variable 周围加上引号以将变量名称与 p 分开来实现它:
sed -n "$first","$count"p imagelist.txt >"$imageblock"
我想将列表拆分为单独的文件夹,并发现最初的问题和答案是一个有用的步骤。(拆分命令不是我必须将代码移植到的旧操作系统的选项)。
只需对上面给出的 3 个解决方案进行基准测试,这对我有用:
3 个解决方案的学分归于:
我正在使用在我的服务器中找到的一个大文件:
# wc fo2debug.1.log
10421186 19448208 38795491134 fo2debug.1.log
38 Gb,1040 万行。
是的,我有一个 logrotate 问题。:))
从文件开头获取 256 行。
# time sed -n '1001,1256p;1256q' fo2debug.1.log | wc -l
256
real 0m0,003s
user 0m0,000s
sys 0m0,004s
# time head -1256 fo2debug.1.log | tail -n +1001 | wc -l
256
real 0m0,003s
user 0m0,006s
sys 0m0,000s
# time awk 'NR==1001, NR==1256; NR==1256 {exit}' fo2debug.1.log | wc -l
256
real 0m0,002s
user 0m0,004s
sys 0m0,000s
奥克赢了。sed 和“head+tail”在技术上并列第二。
在文件的前三分之一末尾获得 256 行。
# time sed -n '3473001,3473256p;3473256q' fo2debug.1.log | wc -l
256
real 0m0,265s
user 0m0,242s
sys 0m0,024s
# time head -3473256 fo2debug.1.log | tail -n +3473001 | wc -l
256
real 0m0,308s
user 0m0,313s
sys 0m0,145s
# time awk 'NR==3473001, NR==3473256; NR==3473256 {exit}' fo2debug.1.log | wc -l
256
real 0m0,393s
user 0m0,326s
sys 0m0,068s
赛德赢了。接着是“头+尾”,最后是 awk。
在文件的第二个三分之一的末尾获得 256 行。
# time sed -n '6947001,6947256p;6947256q' fo2debug.1.log | wc -l
A256
real 0m0,525s
user 0m0,462s
sys 0m0,064s
# time head -6947256 fo2debug.1.log | tail -n +6947001 | wc -l
256
real 0m0,615s
user 0m0,488s
sys 0m0,423s
# time awk 'NR==6947001, NR==6947256; NR==6947256 {exit}' fo2debug.1.log | wc -l
256
real 0m0,779s
user 0m0,650s
sys 0m0,130s
结果相同。
赛德赢了。接着是“头+尾”,最后是 awk。
在文件末尾附近获得 256 行。
# time sed -n '10420001,10420256p;10420256q' fo2debug.1.log | wc -l
256
real 1m50,017s
user 0m12,735s
sys 0m22,926s
# time head -10420256 fo2debug.1.log | tail -n +10420001 | wc -l
256
real 1m48,269s
user 0m42,404s
sys 0m51,015s
# time awk 'NR==10420001, NR==10420256; NR==10420256 {exit}' fo2debug.1.log | wc -l
256
real 1m49,106s
user 0m12,322s
sys 0m18,576s
突然,一个转折!
“头+尾”赢了。接着是 awk,最后是 sed。
(几个小时后……)
我上面的分析最终成为分析中基本缺陷的一个例子。
缺陷在于不深入了解用于分析的资源。
在这种情况下,我使用了一个日志文件来分析在其中搜索一定数量的行的性能。
使用 3 种不同的技术,在文件中的不同点进行搜索,比较技术在每个点的性能,并检查结果是否因文件中进行搜索的点而异。
我的错误是假设日志文件中的内容具有一定的同质性。
现实情况是,长行出现在文件末尾的频率更高。
因此,用给定的技术,更长的搜索(更接近文件的末尾)更好的明显结论可能是有偏见的。事实上,这种技术在处理较长的行时可能会更好。什么还有待确认。
我正要发布头/尾技巧,但实际上我可能只是启动了 emacs。;-)
打开新的输出文件,ctl-y保存
让我看看发生了什么。
我会使用:
awk 'FNR >= 16224 && FNR <= 16482' my_file > extracted.txt
FNR 包含从文件中读取的行的记录(行)号。
使用编辑:
ed -s infile <<<'16224,16482p'
-s
抑制诊断输出;实际命令位于此处的字符串中。具体来说,在所需的行地址范围上16224,16482p
运行(打印)命令。p
我写了一个小 bash 脚本,你可以从命令行运行它,只要你更新你的 PATH 以包含它的目录(或者你可以把它放在一个已经包含在 PATH 中的目录中)。
用法:$ pinch filename start-line end-line
#!/bin/bash
# Display line number ranges of a file to the terminal.
# Usage: $ pinch filename start-line end-line
# By Evan J. Coon
FILENAME=$1
START=$2
END=$3
ERROR="[PINCH ERROR]"
# Check that the number of arguments is 3
if [ $# -lt 3 ]; then
echo "$ERROR Need three arguments: Filename Start-line End-line"
exit 1
fi
# Check that the file exists.
if [ ! -f "$FILENAME" ]; then
echo -e "$ERROR File does not exist. \n\t$FILENAME"
exit 1
fi
# Check that start-line is not greater than end-line
if [ "$START" -gt "$END" ]; then
echo -e "$ERROR Start line is greater than End line."
exit 1
fi
# Check that start-line is positive.
if [ "$START" -lt 0 ]; then
echo -e "$ERROR Start line is less than 0."
exit 1
fi
# Check that end-line is positive.
if [ "$END" -lt 0 ]; then
echo -e "$ERROR End line is less than 0."
exit 1
fi
NUMOFLINES=$(wc -l < "$FILENAME")
# Check that end-line is not greater than the number of lines in the file.
if [ "$END" -gt "$NUMOFLINES" ]; then
echo -e "$ERROR End line is greater than number of lines in file."
exit 1
fi
# The distance from the end of the file to end-line
ENDDIFF=$(( NUMOFLINES - END ))
# For larger files, this will run more quickly. If the distance from the
# end of the file to the end-line is less than the distance from the
# start of the file to the start-line, then start pinching from the
# bottom as opposed to the top.
if [ "$START" -lt "$ENDDIFF" ]; then
< "$FILENAME" head -n $END | tail -n +$START
else
< "$FILENAME" tail -n +$START | head -n $(( END-START+1 ))
fi
# Success
exit 0
这可能对您有用(GNU sed):
sed -ne '16224,16482w newfile' -e '16482q' file
或利用 bash:
sed -n $'16224,16482w newfile\n16482q' file
由于我们正在讨论从文本文件中提取文本行,因此我将给出一个特殊情况,您希望提取与特定模式匹配的所有行。
myfile content:
=====================
line1 not needed
line2 also discarded
[Data]
first data line
second data line
=====================
sed -n '/Data/,$p' myfile
将打印 [Data] 行和剩余部分。如果您想要从 line1 到模式的文本,请键入:sed -n '1,/Data/p' myfile。此外,如果您知道两个模式(最好在您的文本中是唯一的),则可以使用匹配项指定范围的开始行和结束行。
sed -n '/BEGIN_MARK/,/END_MARK/p' myfile
接受答案中的 -n 有效。如果您愿意,这是另一种方式。
cat $filename | sed "${linenum}p;d";
这将执行以下操作:
我一直在寻找这个问题的答案,但我最终不得不编写自己的代码。上面的答案没有一个是令人满意的。考虑您有非常大的文件并且有某些要打印的行号,但这些数字不按顺序排列。您可以执行以下操作:
我比较大的文件
for letter in {a..k} ; do echo $letter; done | cat -n > myfile.txt
1 a
2 b
3 c
4 d
5 e
6 f
7 g
8 h
9 i
10 j
11 k
我想要的特定行号:
shuf -i 1-11 -n 4 > line_numbers_I_want.txt
10
11
4
9
要打印这些行号,请执行以下操作。
awk '{system("head myfile.txt -n " $0 " | tail -n 1")}' line_numbers_I_want.txt
上面所做的是将 n 行放在前面,然后使用 tail 取最后一行
如果您希望按顺序排列行号,请先排序(是 -n 数字排序)然后获取行。
cat line_numbers_I_want.txt | sort -n | awk '{system("head myfile.txt -n " $0 " | tail -n 1")}'
4 d
9 i
10 j
11 k