如何使用Shell命令删除文件的前n行和后行?


31

我有一个名为Element_query包含查询结果的文件:

SQL> select count (*) from element;

[Output of the query which I want to keep in my file]

SQL> spool off;

我想使用shell命令删除第一行和最后一行。


2
您可能最好在SQL * Plus中解决此问题;您可以告诉SQL * Plus不要首先生成该文件,而不是生成文件然后尝试修剪您不需要的文件。docs.oracle.com/cd/A84870_01/doc/sqlplus.816/a75664/ch44.htm上的“创建平面文件”一节中介绍了一种方法;在stackoverflow.com/q/2299375/978917中描述了另一种方法。
ruakh 2015年

Answers:


48

使用GNU sed

sed -i '1d;$d' Element_query

怎么运行的 :

  • -i选项编辑文件本身。您也可以删除该选项,并根据需要将输出重定向到新文件或其他命令。
  • 1d删除第一行(1仅作用于第一行,d将其删除)
  • $d删除最后一行($仅作用于最后一行,d将其删除)

更进一步:

  • 您也可以删除范围。例如,1,5d将删除前5行。
  • 您还可以删除以SQL>以下语句开头的每一行/^SQL> /d
  • 您可以使用删除每个空白行 /^$/d
  • 最后,您可以使用分号(statement1;statement2;satement3;...)或在命令行(-e 'statement1' -e 'statement 2' ...)分别指定它们来组合任何语句

如果要删除其第三行...那么我必须使用3d代替1d?如果它的第三行从最后一个删除...那么命令是什么?
pmaipmui 2015年

如何使用shell命令从最后删除第三行?
pmaipmui 2015年

@Nainita您可以指定一个范围(1,3d将删除前三行),但结束时会有些困难。根据您的需要,最好使用this:sed -i '/^SQL> /d' Element_query删除SQL> 以文件开头的行,而不管它在文件中的什么位置。
user43791 2015年

@Nainita- 有关任意尾数的信息,请参见我的答案 -它提供了两种相对于文件末尾剥离计数线的解决方案。一种是sed单线-它将用于从文件的开头结尾中剥离任意行数,但是更好的是,只要输入是常规文件,就是将单个输入跨两个head进程进行分组-通常最快的方法。
mikeserv

我曾经sed -i '1d' table-backup.sql删除了SQL文本文件的第一行
David Thomas

8

头; 头

{   head -n[num] >/dev/null
    head -n[num]
}  <infile >outfile

使用上面的命令,您可以指定要从输出的开头去除第一个head命令的行的第一个行数,以及要写入outfile第二个命令的行数。尽管需要两次调用,但它通常比sed-特别是在输入较大时 - 更快地执行此操作。当sed绝对应该是首选,虽然,在该情况下,<infile不是有规律的,lseekable文件-因为这通常不会如预期在这种情况下,但工作sed可以处理在一个单一的,脚本化过程中所有的输出修改。

使用GNU,head您也可以在第二个命令中使用-否定形式[num]。在这种情况下,以下命令将从输入中删除第一行和最后一行:

{   head -n1 >/dev/null
    head -n-1
}  <infile >outfile

或使用POSIX sed

例如,假设我正在读取20行的输入,并且想剥离前3条和最后7条。如果我决定这样sed做,则将使用尾部缓冲区。我首先将三个和七个加在一起,得出总共十个试纸条,然后执行以下操作:

seq 20 | sed -ne:n -e '3d;N;1,10bn' -eP\;D

这是从输入中去除前3行和后7行的示例。这个想法是,您可以在堆栈的模式空间中缓冲希望从输入的尾部剥离的任意多行,但P对于拉入的每一行,只保留第一个行。

  • 在线上1,10 sed P什么都不会漂洗,因为对于每一种,它都是在b牧场循环中逐行地将输入堆叠在模式空间中。
  • 在第3行上,所有sed的堆栈d都被删除-因此前3行在一次下降中被从输出中剥离。
  • sed到达$输入的最后一行并尝试拉入Next时,它将到达EOF并完全停止处理。但是那时模式空间包含所有行14,20-没有行被P漂洗过,从没有被洗掉。
  • 在每隔一行sed P上,仅\n刷新到模式空间中第一个出现的ewline,并D在开始新的循环之前将其删除,而剩下的就是-或接下来的6行输入。N在新循环中,使用ext命令将第7行再次追加到堆栈中。

因此,在seq的输出(按顺序编号的20行)中sed仅输出:

4
5
6
7
8
9
10
11
12
13

当您希望从输入的尾部剥离的行数很大时,这将成为问题-因为sed的性能直接与其模式空间的大小成正比。不过,在许多情况下,它仍然是可行的解决方案-POSIX规定了sed模式空间以在破坏前至少处理4kb。


1
gnu tail还支持扩展tail -n+<num>语法,意思是“从行开始<num>
UloPe

4

我不会回答如何删除许多行。我将以这种方式解决问题:

grep -v '#SQL>' Element_query >outfile

它不用计数行,而是通过识别提示来消除SQL命令。然后,该解决方案可以推广到SQL会话的其他输出文件,而不仅仅是两个命令。


我喜欢。我对SQL不太了解-但是是否没有机会在其输出行的开头出现提示?
mikeserv

4

ed是“标准文本编辑器”,应该在没有GNU的系统上可用sed。它最初设计为文本编辑器,但非常适合脚本编写。

printf '%s\n' 1d '$d' w q | ed Element_query

1d删除文件的第一行$d(加引号,以便外壳程序不认为它是变量),删除最后一行,w写入文件并q退出edprintf用于格式化以下命令的格式ed-每个命令后面都必须有换行符;当然,还有其他方法可以做到这一点。


3

有几种方法可以从文件中删除开头和结尾行。

您可以使用awk它来处理模式匹配和行计数,

#you need to know length to skip last line, assume we have 100 lines
awk 'NR>1 && NR<100 {print $0};' < inputfile
#or
awk '/SQL/ {next}; {print $0;};' < inputfile |awk 'NR>1&& NR<10 {print $0};'

您可以使用模式grep -v来排除不需要的行,也可以使用-E选项匹配多个模式,

grep -v -E "SQL>" < inputfile > outputfile

您可以使用headtail修剪特定的行数,

lines=$((`wc -l < inputfile`-2)) #how many lines in file, less 2
head -n-1 < inputfile | head -n$lines > outputfile
#or
tail -n+2 < inputfile | head -n$lines > outputfile

您可以使用vi/vim,并删除第一行和最后一行,

vi inputfile
:1
dd
:$
dd
:w! outputfile
:x

您可以使用perl脚本,跳过第一行,保存每一行,在获得下一行时打印,

#left as exercise for the reader :-)

1
对于heads,您实际上并不需要管道,实际上,如果可以使用它,最好不要使用它。当您这样做时head | head-虽然两个进程可以同时运行,但是它们实际上都冗余地处理了所有相同的数据。如果您改为这样做,则{ head >dump; head >save; } <in只能跳过偏移量-第一个向读取10行,>dump第二个向读取接下来的 10行>save
mikeserv

3

你会好得多通过服务切掉的SQL命令。您可以通过两种方式执行此操作:

  1. 如果你是绝对肯定的是,序列“ SQL>”并没有发生其他任何地方输出,

    grep -v -F 'SQL> ' < infile > outfile
  2. 如果您不确定,

    grep -v '^SQL> .*;$' < infile > outfile

第二个版本比较慢,但更准确:它将忽略以“ SQL>”开头并以分号结尾的行,这些行似乎描述了您要消除的行。

但是,最好不要将多余的输出放在文件中。大多数SQL系统都有这样做的方法。我不太了解Oracle,但是也许这个答案可能会有所帮助。


3

您可以选择一个范围内的行awk(假设您知道有多少行):

awk 'NR>1 && NR < 3' file

或在Perl中:

perl -ne 'print if $.>1 && $.<3' file

如果您不知道有多少行,可以使用即时计算grep(请注意,这不会计算空白行,也可以使用grep -c '' file它们来计算):

awk -vm="$(grep -c . file2.txt)" 'NR>1 && NR<m' file2.txt

3

试试这个解决方案:

tail -n +2 name_of_file | head -n-1

客制化

您可以轻松地适应它给删除了n个第一线改变+2tail;
或删除最后n行改变-1head


此解决方案不正确,因为它打印第一行。
xhienne

1
@xhienne抱歉,这是一个错误。我写了1而不是2作为“ tail”的参数。现在可以了,谢谢!:)
Gabrer '17

1

使用awk

< inputfile awk 'NR>1 {print r}; {r=$0}' > outputfile
  • < inputfile:将的内容重定向inputfileawkstdin
  • > outputfile:重定向的内容awkstdout,以outputfile
  • NR>1:仅当正在处理的记录数大于1时才执行以下操作
  • {print r}:打印变量的内容 r
  • {r=$0}:将正在处理的记录的内容分配给变量 r

因此,在第一次执行awk脚本时,不执行第一个动作块,而执行第二个动作块,并且将记录的内容分配给变量r;在第二次执行时,将执行第一个动作块,并r打印变量的内容(因此将打印先前的记录);这具有打印每条处理过的行(但第一行和最后一行)的效果。


您不排除第一行。在NR == 2时,打印存储在中的输入的第一行r
xhienne
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.