如何使用sed仅查找文件的第一行和最后一行

时间:2020-07-08 06:23:23

标签: sed

我有一个名为error_log的apache文件,我想使用sed命令查看该文件的第一行和最后一行。您能帮我怎么做?

我知道如何使用headtail命令来执行此操作,但是我很好奇在sed命令中是否也可以。

我已经读过man sed并在Google上搜索了很多,但不幸的是没有找到任何东西。

4 个答案:

答案 0 :(得分:1)

第一行:

sed '2,$d' error_log

最后一行:

sed '$!d' error_log

答案 1 :(得分:1)

这将起作用:

sed -n '1p ; $p' error_log

1p将打印第一行,$p将打印最后一行。

作为建议,不但man sed,而且要看info sed。您可以在第2.1段中找到有关您的问题的一些示例。

答案 2 :(得分:1)

这可能对您有用(GNU sed):

sed '1b;$b;d' file

所有sed命令都可以以地址或正则表达式为前缀。地址是行号或代表最后一行的$。如果没有地址或正则表达式,则以下命令将应用于所有其他行。

正常sed周期在模式空间中显示每行输入(减去换行)。然后应用sed命令,循环的最后一步是重新附加换行符并打印结果。

b命令控制命令流;如果它本身跳出了以下sed命令,就跳到循环的最后一个动作,即重新连接换行符并打印结果。

d命令删除了模式空间,并且由于没有要打印的内容,因此不执行进一步的处理(包括重新附加换行符和打印结果)。

因此上述解决方案将打印第一行和最后一行,并删除其余行。

Sed具有一些命令行选项,其中之一是模式空间-n的隐式打印结果。 p命令显示模式空间的当前状态。因此,上述解决方案的对偶是:

sed -n '1p;$p' file

如果输入文件只有一行,则第一种解决方案将只打印一行,而第二种解决方案将同一行打印两次。同样,如果输入了多个文件,则除非使用-i选项,否则两种解决方案都将打印第一个文件的第一行和最后一个文件的最后一行,在这种情况下,将修改每个文件。 -s选项可在不修改每个文件的情况下复制此文件,但会将结果流式传输到stdout,就像每个文件都被单独处理一样。

答案 3 :(得分:1)

根据您的新要求,如果输入文件只有1行,则什么也不输出(请参见How to find only the first and last line of a file using sed):

awk 'NR==1{first=$0} {last=$0} END{if (NR>1) print first ORS last}'

原始答案:

从表面上看,这是您可以在sed中轻松完成的事情之一:

$ seq 3 7
3
4
5
6
7

$ seq 3 7 | sed -n '1p; $p'
3
7

,但是如何处理像输入的一行这样的边缘情况却不是很明显,例如这真的是正确的输出吗?

$ printf 'foo\n' | sed -n '1p; $p'
foo
foo

还是正确的输出:

foo

,如果是后者,如何调整sed命令以产生输出? @potong建议使用GNU sed命令:

$ printf 'foo\n' | sed '1b;$b;d'
foo

可以工作,但可能仅适用于GNU(idk),更重要的是,它看起来与我们开始使用的命令不太相似,因此对需求的最小更改意味着可以使用不同的构造完全重写。

现在,如果要增强它,例如仅在文件包含foo的情况下才打印第一行和最后一行,该怎么办?我希望这将是sed的又一项挑战性练习,并且可能还涉及非便携式构造。

当您可以使用诸如awk之类的其他工具并以简单,一致,可移植的语法来完成您想做的事情时,学习如何使用sed进行操作就毫无意义了

$ seq 3 7 |
awk 'NR==1{first=$0} {last=$0} END{print first ORS last}'
3
7

$ printf 'foo\n' |
awk 'NR==1{first=$0} {last=$0} END{print first ORS last}'
foo
foo

$ printf 'foo\n' |
awk 'NR==1{first=$0} {last=$0} END{print first (NR>1 ? ORS last : "")}'
foo

$ printf '3\nfoo\n7\n' |
awk 'NR==1{first=$0} /foo/{f=1} {last=$0} END{if (f) print first (NR>1 ? ORS last : "")}'
3
7

$ printf '3\nbar\n7\n' |
awk 'NR==1{first=$0} /foo/{f=1} {last=$0} END{if (f) print first (NR>1 ? ORS last : "")}'
$

注意:

  1. 每个命令看起来都和其他命令一样。
  2. 需求上的微小变化会导致代码上的微小变化,而不是完整的重写。
  3. 一旦您学会了如何进行给定的事情A,如何去做类似的事情B,C,D等,只需在已经使用的语法基础上进行构建,就不必学习完全不同的语法。
  4. 这些命令中的每一个都可以在每个UNIX盒的任何shell中使用任何awk来工作。

现在,如果要对多个文件(如以下命令创建的文件)执行该操作,怎么办?

$ seq 3 7 > file1
$ seq 12 25 > file2

使用awk,您只需将行存储在数组中即可在END中打印:

$ awk 'FNR==1{first[++cnt]=$0} {last[cnt]=$0}
    END{for (i=1;i<=cnt;i++) print first[i] ORS last[i]}' file1 file2
3
7
12
25

或使用GNU awk,您可以从ENDFILE打印它们:

$ awk 'FNR==1{first=$0} {last=$0} ENDFILE{print first ORS last}' file1 file2
3
7
12
25

用sed吗?留给读者的练习。