代码之家  ›  专栏  ›  技术社区  ›  Saeed

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

sed
  •  0
  • Saeed  · 技术社区  · 5 年前

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

    我知道怎么做 head tail 命令,但我很好奇这是否可能 sed 命令也是。

    我读过 man sed 我在谷歌上搜索了很多,但不幸的是什么也没找到。

    1 回复  |  直到 5 年前
        1
  •  4
  •   potong    5 年前

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

    sed '1b;$b;d' file
    

    所有sed命令都可以以地址或正则表达式作为前缀。地址可以是行号或 $ 它代表最后一行。如果地址或正则表达式都不存在,则以下命令适用于所有其他行。

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

    这个 b 命令控制命令流;如果它本身跳出以下sed命令,跳到循环的最后一步,即重新附加换行符并打印结果。

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

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

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

    sed -n '1p;$p' file
    

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

        2
  •  2
  •   Francesco    5 年前

    这将奏效:

    sed -n '1p ; $p' error_log
    

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

    作为建议,请查看 info sed ,不仅 man sed 。你可以在第2.1段中找到关于你的问题的一些例子。

        3
  •  1
  •   Necklondon    5 年前

    第一行:

    sed '2,$d' error_log
    

    最后一行:

    sed '$!d' error_log
    
        4
  •  1
  •   Ed Morton    5 年前

    根据您的新要求,如果输入文件只有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?留给读者的练习。