如何仅保留文件的第n行


71

我有一个相当大的CSV文件(75MB)。我只是想生成一个图形,所以我真的不需要所有数据。

重新措辞:我想删除n行,然后保留一行,然后删除n行,依此类推。

因此,如果文件如下所示:

Line 1
Line 2
Line 3
Line 4
Line 5
Line 6

并且n = 2,则输出为:

Line 3
Line 6

似乎sed可以做到这一点,但我还没办法弄清楚。bash命令将是理想选择,但是我愿意接受任何解决方案。


2
您是否真的要1、3、6等行,而不是1、4、7等行?
Ilmari Karonen

2
由于它是CSV文件,因此我假设第一行包含元数据(即字段名称)。如果是这样,问题应该是“第一行之后的第n行”。
iglvzx 2012年

7
1、3、6仍然没有意义!
2012年

1
我猜应该是1、3、5,除非n = 2是三角数(
1、3、6、10、15、21

4
您可以更新您的问题以使您的要求(“每第n行”,“ n = 2”)和所需的输出(第3行,第6行)保持一致吗?未来的读者会感到困惑。
基思·汤普森

Answers:


121
~ $ awk 'NR == 1 || NR % 3 == 0' yourfile
Line 1
Line 3
Line 6

NR(记录数)变量是记录的行数,因为默认行为是RS(记录分隔符)的新行。模式和动作是awk的默认格式,是可选的'pattern {actions}'。当我们只提供模式部分时,然后为模式条件awk写入所有字段。 $0true


8
多亏了默认设置,您甚至不需要那么多:awk 'NR == 1 || NR % 3 == 0'
凯文(Kevin)

@selman:如果您喜欢Kevin的解决方案,则可能需要考虑更新答案。
基思·汤普森

4
认真解释为什么会这样吗?这样,如果有人要稍作调整,那么希望您的解释会帮助他们这样做
Ivo Flipse 2012年

我发现这种方法使第1行和第2行保持不变。可以肯定的是awk 'NR == 1 || NR % 2 == 0' myfile.txt | wc -l,原始文件的行数为偶数,结果是奇数。@kev答案在我的测试案例中效果最好。
Daniel Da Cunha 2015年

58

sed 也可以这样做:

$ sed -n '1p;0~3p' input.txt
Line 1
Line 3
Line 6

man sed解释~为:

first〜step匹配从第一行开始的每一步。例如,``sed -n 1〜2p''将打印输入流中的所有奇数行,并且地址2〜5将匹配从第五行开始的第二行。首先可以为零;在这种情况下,sed的运行就好像与step相同。(这是一个扩展。)


6
您能解释一下该命令吗?
2014年

1
@qed说明:1p打印第一行,0~3p从第三行开始每三行打印一次(1p因此需要打印第一行)。但是请注意,这0~3不是标准的而是GNU sed扩展。
2015年

“这是扩展。” 您正在/正在使用哪个版本?
维克多

对于Windows PowerShell,此答案对我很有帮助。我这样扩展它:sed -n '1p;0~10p' '.\in.txt' > out.txt将缩小的文件打印到输出文件中。
kimliv

22

Perl也可以这样做:

while (<>) {
    print  if $. % 3 == 1;
}

该程序将打印其输入的第一行,然后每三行打印一次。

稍微解释一下的<>是行输入运算符,它在这样的while循环中使用时会在输入行上进行迭代。特殊变量$.包含到目前为止读取的行数,并且%是模数运算符。

使用-n-e开关,可以将这些代码更紧凑地编写为单行代码:

perl -ne 'print if $. % 3 == 1'  < input.txt  > output.txt

-e开关需要一段Perl代码作为命令行参数执行,而该-n开关则隐式地将代码包装在一个while如上所示的循环中。


编辑:要实际获得第1、3、6、9,...行,如本例所示,而不是第1、4、7、10,...行,如我首先假设的那样,请替换$. % 3 == 1$. == 1 or $. % 3 == 0


7

如果要使用Bash脚本执行此操作,可以尝试:

#!/bin/sh

echo Please enter the file name
read fname
echo Please enter the Nth lines that you want to keep
read n

exec<$fname
value=0
while read line
do
    if [ $(( $value % $n )) -eq 0 ] ; then
        echo -e "$line" >> new_file.txt
    fi
        let value=value+1 
done
echo "Check the 'new_file.txt' that has been created in this directory";

将其另存为“ read_lines.sh”,并记住为bash文件提供+ x权限。

chmod +x ./read_lines.sh

1
如果您只是在标准输出上发出此消息,请从参数中跳过行号以从标准输入中跳过,并从标准输入中读取文件,这将更加简单和有用。您仍然可以通过这样做来创建new_file.txt ./read_lines.sh > new_file.txt
rjmunro 2012年

4

一个纯bash解决方案,它不产生进程:

{ for f in {1..2}; do read line; done;
  while read line; do
    echo $line;
    for f in {1..2}; do read line; done;
  done; } < file

第一行在文件开头跳过2行,然后while打印下一行并再次跳过2行。

如果文件很小,这是完成任务的非常有效的方法,因为它不会启动进程。当您的文件很大时,sed应使用它,因为它处理io的效率要高于bash


1

Python版本(Python 2和Python 3):

python2 -c "print(''.join(open('file.txt').readlines()[::3]))"

替换[::3]为开始,结束和步长参数以进行更多控制。例如[10:36:5],输出第10,15,...,35行。

注意,由于readlines()保留了行尾,因此此调用的输出可能以空的最后一行结束,除非原始的最后一行被选定的步长放出。

流版本也是可能的(此处仅在完成流之后输出):

python -c "import sys;print(''.join(list(sys.stdin)[::3]))" < file.txt
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.