【问题标题】:How can I delete the lines starting with "//" (e.g., file header) which are at the beginning of a file?如何删除文件开头的以“//”开头的行(例如文件头)?
【发布时间】:2020-10-31 14:17:45
【问题描述】:

我想从所有文件中删除标头,标头有以//开头的行。

如果我想删除所有以// 开头的行,我可以这样做:

sed '/^\/\//d'

但是,这不是我需要做的事情。我只需要删除文件开头以// 开头的行。

示例文件:

// This is the header
// This should be deleted
print "Hi"
// This should not be deleted
print "Hello"

预期输出:

print "Hi"
// This should not be deleted
print "Hello"

更新: 如果开头或中间有新行,则不起作用。有没有办法处理这种情况?

示例文件:

< new empty line >
// This is the header
< new empty line >
// This should be deleted
print "Hi"
// This should not be deleted
print "Hello"

预期输出:

print "Hi"
// This should not be deleted
print "Hello"

有人可以建议一种方法吗?提前致谢!

更新:接受的答案在开头或中间对white space 效果很好。

【问题讨论】:

  • 请在您的问题中添加示例输入(无描述、无图像、无链接)和该示例输入所需的输出(无评论)。
  • 感谢您的建议,我已经更新了问题。
  • 如果没有初始的cmets,是否应该删除文件开头的任何空行?
  • 是的,可以在开头或标题之间有空行。这更像是一个一致性问题。某些情况下,可以有空格,然后在标题中//
  • 基本上这就是你问(?m)(?:^\s*//.*\s*){2} 的原因,因为我不熟悉 sed 或 awk 的工作原理,所以我将它放在评论中,没有任何解释。但是,如果您尝试任何不能执行此确切功能的操作,它将无法正常工作。

标签: regex shell awk sed


【解决方案1】:

使用ed编辑器sed所基于的文件编辑器),

printf '1,/^[^/]/ g|^\(//.*\)\{0,1\}$| d\nw\n' | ed tmp.txt

一些解释可能是有序的。

ed 将要编辑的文件名作为参数,并从标准输入读取命令。每个命令都以换行符终止。 (您也可以从此处的文档中读取命令,而不是通过管道从 printf 中读取。)

  1. 1,/^[^/]/ 寻址文件中的第一行,直到并包括第一行/ 开头的行。 (您要删除的所有行都将包含在此集合中。)
  2. g|^\(//.*\)\{0,1\}$|d 删除所有空的或确实//开头的寻址行。
  3. w 保存更改。

第 2 步有点难看;不幸的是,ed 不支持您可能认为理所当然的正则表达式运算符,例如 ?|。稍微分解一下正则表达式:

  1. ^ 匹配行首。
  2. //.* 匹配 // 后跟零个或多个字符。
  3. \(//.*\)\{0,1\} 匹配前面的正则表达式 0 或 1 次(即可选)
  4. $ 匹配行尾。

【讨论】:

    【解决方案2】:

    我听起来你只是想从第一行开始打印,既不是空白也不是评论:

    $ awk 'NF && ($1 !~ "^//"){f=1} f' file
    print "Hi"
    // This should not be deleted
    print "Hello"
    

    上面只是在找到这样的行时设置一个标志f 并从那时起打印每一行。它可以在每个 UNIX 机器上的任何 shell 中使用任何 awk。

    请注意,与发布的一些潜在解决方案不同,它一次不会在内存中存储超过 1 行,因此无论您的输入文件有多大,它都能正常工作。

    已针对此输入进行了测试:

    $ cat file
    
        // This is the header
    
    // This should be deleted
    print "Hi"
    // This should not be deleted
    print "Hello"
    

    使用 GNU awk 一次在多个文件上运行上述内容并随时修改每个文件:

    awk -i inplace 'NF && ($1 !~ "^//"){f=1} f' *
    

    这与任何 awk:

    ip_awk() { local f t=$(mktemp) && for f in "${@:2}"; do awk "$1" "$f" > "$t" && mv -- "$t" "$f"; done; }
    
    ip_awk 'NF && ($1 !~ "^//"){f=1} f' *
    

    【讨论】:

      【解决方案3】:

      sed:

      sed -n '1{:a; /^[[:space:]]*\/\/\|^$/ {n; ba}};p' file
      print "Hi"
      // This should not be deleted
      print "Hello"
      

      带有GNU sed的略短版本:

      sed -nE '1{:a; /^\s*\/\/|^$/ {n; ba}};p' file
      

      解释:

      1 { # execute this block on the fist line only
          :a; # this is a label
           /^\s*\/\/|^$/ { n;  # on lines matching `^\s*\/\/` or `^$`, do: read the next line
                ba }           # and go to label :a
      };  # end block
      p   # print line unchanged:
          # we only get here after the header or when it's not found
      

      sed -n 使sed 在没有p 命令的情况下不打印任何行。

      编辑:更新了模式以跳过空行。

      【讨论】:

      • 这适用于 GNU sed。知道需要进行哪些调整才能使其与 BSD 和/或 POSIX sed 一起使用吗?
      • @chepner 我不确定,但我的手册页上写着-E 并且 POSIX 支持 ERE?
      • 我认为你必须为POSIX sed 使用 BRE,但这似乎很容易解决(GNU sed -n '...' 只要你避开| 就可以工作)。 BSD sed 似乎是麻烦制造者。
      • \s 绝对是 GNU 扩展;你可以改用[[:space:]],但是如果你知道你使用的是GNU sed,那肯定不太方便。
      • @Sweety 你知道没有就地编辑这样的东西吗?当您执行sed -i 'script' 时,它不会就地编辑文件,它只是在内部制作文件的副本并稍后用该副本覆盖原始文件。您可以通过编写 in_cmd() { for file; do cmd 'script' "$file" &gt; tmp &amp;&amp; mv tmp "$file"; done; }; in_cmd * 对任何命令 cmd 执行相同操作不要牺牲您使用的工具/脚本来进行“就地”编辑,因为这是任何程序的简单部分。
      【解决方案4】:

      如果perl 可用,那么这也可以在 slurp 模式下工作:

      perl -0777 -pe 's~\A(?:\h*(?://.*)?\R+)+~~' file
      

      \A 将仅匹配文件的开头,(?:\h*(?://.*)?\R+)+ 将匹配 1 行或多行空白或 // 带有可选的前导空格。

      【讨论】:

        【解决方案5】:

        请您尝试以下操作。这也考虑了新的线路场景,在https://ideone.com/IKN3QR 中编写和测试

        awk '
        (NF == 0 || /^[[:blank:]]*\/\//) && !found{
          next
        }
        NF{
          found=1
        }
        1
        ' Input_file
        

        解释:简单地检查条件,如果一行是空的,或者从// 开始并且发现的变量是NULL,然后简单地跳过这些行。一旦找到任何没有// 的行,然后在此处找到设置变量,因此所有接下来的行都应该从设置为的行打印到 Input_file 打印的末尾。

        【讨论】:

        • 快到了!对于这个更新的问题,您的答案是有效的。会接受的。我想知道是否有办法在标题中的// 之前处理white space?我在添加空间后尝试了您的解决方案,但没有奏效。
        • 您希望/^[[:blank:]]*\/\// 以注释开始 行,而不仅仅是包含 注释。
        • 我更改了“空行”条件,以防空行包含空格。
        • 简单有效++
        • @Sweety,是的,添加了它,它似乎正在工作,如果有任何疑问,请让我知道。
        【解决方案6】:

        使用 GNU sed:

        sed -i -Ez 's/^((\/\/[^\n]*|\s*)\n)+//' file
        

        ^((\/\/[^\n]*|\s*)\n)+ 表达式将匹配以// 开头的一行或多行,也匹配空行,仅在文件开头。

        【讨论】:

        • 这不是我想要的。它从我的文件中删除所有以// 开头的行。我想要的是:只有以// 开头的文件的标题应该被删除,而不是任何其他以// 开头的行。
        • @Sweety 我用 GNU sed 解决方案更新了答案。
        • 上面写着sed: invalid option -- 'z'
        • @Sweety 这意味着你的 sed 不是 GNU sed。我也在开头添加了空行匹配。