删除每个注释块除最后一个注释行之外的所有注释行

删除每个注释块除最后一个注释行之外的所有注释行
  • 目标:删除每个注释块中除最后一行以外的所有注释行。如果文件以注释块结尾,请将其完全删除。每个注释行都以#.

  • 我尝试过的命令

    sed -z -e 's/#.*\n#/#/g' "${InputP}"
    
  • 输入文件

    # Life/Living
    # Life/Passion
    - [Mindfulness.md](file:///home/nikhil/Documents/Git/Life/Passion/PassionSrc/Sports/Yoga/Mindfulness/Mindfulness.md)
    # Life/PersonalManagement
    # Life/Social
    # Linux/AmazingNotes
    # Linux/Backintime
    # Linux/DotFiles
    # Linux/GitScripts
    - [Peaceful.m3u](file:///home/nikhil/Documents/Git/../Mobile/Documents/PortableNotes/PortableNotesSrc/SocialActivity/Music/SongsPlaylist/Data/Peaceful.m3u)
    - [AuxiliaryFiles.sh](file:///home/nikhil/Documents/Git/Linux/GitScripts/GitScriptsSrc/GitInit/GitNew/Src/AuxiliaryFiles.sh)
    # PythonWs/NumericalProgramming
    # PythonWs/Python
    # PythonWs/ScientificComputing
    
  • 预期输出

    # Life/Passion
    - [Mindfulness.md](file:///home/nikhil/Documents/Git/Life/Passion/PassionSrc/Sports/Yoga/Mindfulness/Mindfulness.md)
    # Linux/GitScripts
    - [Peaceful.m3u](file:///home/nikhil/Documents/Git/../Mobile/Documents/PortableNotes/PortableNotesSrc/SocialActivity/Music/SongsPlaylist/Data/Peaceful.m3u)
    - [AuxiliaryFiles.sh](file:///home/nikhil/Documents/Git/Linux/GitScripts/GitScriptsSrc/GitInit/GitNew/Src/AuxiliaryFiles.sh)
    
  • 但我得到这个输出
    # PythonWs/ScientificComputing
    

有谁知道如何解决这个问题?

答案1

问题是它.*是贪婪的,因此sed -z -e 's/#.*\n#/#/g'将从包含 的第一行#开始匹配到以 开头的最后一行#。这只是因为-z标志而发生,它一次吸收模式空间中的整个文件(假设文本文件中没有空字节)。

解决你的问题的 Sed 脚本是

sed -n '/^#/N;/\n#/D;p' file
  • /^#/N如果该行以 开头#,则将下一行追加到模式空间。
  • /\n#/D如果模式空间包含换行符后跟#,则删除换行符之前的所有内容并开始新的循环。
  • p如果到达此命令,则打印模式空间。

有用的链接

答案2

您显然希望从输入中删除后面跟着其他注释行的所有注释行。调用sed失败,因为默认使用正则表达式“贪婪的”(即尽可能多的消费),这是不容易改变的。

所以我将为awk既定目标添加一个基于 - 的解决方案:

awk '/^#/{buf=$0;next} {if (buf) {print buf; buf=""}}1' "${InputP}"

或者,稍微更紧凑:

awk '/^#/{buf=$0;next} buf{print buf; buf=""}1' "${InputP}"
  • 这将打印所有不是注释行的行(1规则块外部意味着“打印当前行,包括迄今为止所做的所有修改” - 在本例中没有)。
  • 如果遇到注释行(该行与模式匹配/^#/),内容将存储在 buffer 中buf,但尚未打印。该next命令会跳到下一行执行,因此其余代码仅适用于非注释行。
  • 如果遇到非注释行,则首先打印缓冲区内容(如果有),并在打印实际行内容之前清空缓冲区(以防止多次打印输出)。

答案3

使用GNU sedslurp 模式-z并利用扩展的正则表达式,-E我们可以执行如下操作:

$ sed -Ez '
    s/(^|\n)(#[^\n]*\n)+$/\1/
    s/(^|\n)(#[^\n]*\n)+/\1\2/g
' file
  • 删除尾随注释块。
  • 删除所有注释块,但保留每个注释块的最后一行。

GNU sed模型如下:

  • sed 逐行读取文件,除非-z有效,否则它会读取整个文件。记录分隔符默认为换行符,\n除非-z正在使用,否则它是\0NULL ascii。
  • 读入记录后,尾随记录分隔符被剪掉,并将结果字符串存储在模式空间寄存器中。模式空间是所有 sed 命令运行的地方。
  • 现在假设sed我们的脚本中有 5 个命令sed。然后将第一个命令应用于模式空间,这会修改模式空间,并在此修改后的模式空间上sed应用下一个命令......依此类推,直到最后一个。然后,stdout除非-n有效,否则将打印模式空间。此后,读入下一条记录并将相同的sed命令序列应用于模式空间。

请注意,上面是一个非常简化的叙述,当脚本中没有使用流控制命令时有效sed

是的,你是对的,在 slurp 模式下,它$表示文件结束,也表示模式空间结束,因为只有一个模式空间。

当您拥有此构造时(regex)+,由于正则表达式的贪婪本质,括号将保存最后一个正则表达式匹配。

或者,也可以这样做

$ sed -e '
    /^#/{h;d;} 
    H;z;x;s/^\n//
' file 

答案4

这应该有效:

perl -ne 'print $x,$_ unless /^#/; $x = /^#/ ? $_ : ""' < infile

我得到了您发布的预期输出。

编辑:解释

  • 如果你愿意的话,就当作$x是:-)$left_over_line_to_be_printed
  • 第一个语句打印任何剩余行,然后打印当前行(如果当前行不是标题行)。
  • 第二条语句将“leftover”设置为最后看到的标题行,或者设置为空字符串。 (也就是说,对于非标题行,“剩余”将是空字符串,因为它已经由第一个语句打印。对于标题行,它将是该行。如果多个标题行聚集在一起,它将最终成为最后一个)。

相关内容