在可能的 shebang 之前删除前导空格

在可能的 shebang 之前删除前导空格

我在一个地方收集了大量非二进制文件。其中一些有 shebang,而其中一些(由于某些莫名其妙的原因)在 shebang 前面有空格。这包括空行和只有空格的行

示例1:

    #!/usr/bin/env foo 酒吧

示例2:

   #!/usr/bin/env foo 酒吧

示例3:

#! /bin/sh -e

示例4:

    ______          
   / ____/___ ____
  / /_ / __ \/ __ \
 / __/ / /_/ / /_/ /
/_/ \____/\____/


这是 Foo News #324,其中包含当天的提示:
不要忘记将 #!/bin/sh 放在你的 shell 脚本文件顶部!

我希望有一个针对基于 GNU (Linux) 系统的解决方案,该解决方案可以删除文件的前导空格,例如 1 和 2,同时保留 3,尤其是 4(即使其中包含类似 shebang 之类的内容)。

示例 1 和 2 将变为:

#!/usr/bin/env foo 酒吧

到目前为止我没有成功的尝试:

  • 第一步尝试区分示例 1-3 和 4:

    grep -Pzo '^[ \t\n]+#! ?[ \w/.-]+'
    

    没有工作,因为grep: unescaped ^ or $ not supported with -Pz.

  • 使用awk

    awk 'BEGIN {ws_check=1} !/[ \t]+/ {ws_check=0}  /#! ?[ \w/.-]+/,0 && ws_check { print }'
    

    为了检测示例 4,而且只用 shebang 打印左侧修剪线的部分,而不修剪其余部分,仍然需要做很多工作。

答案1

perl当且仅当文件中的第一个非空白字符是 shebang 时,我才会将文件放入内存并删除任何前导空白:

perl -i.bak -0pe 's/^\s+(?=#!)//' file

或者,对于许多文件:

for f in ./*; do perl -i.bak -0pe 's/^\s+(?=#!)//' "$f"; done

(?=#!)是一个积极的前瞻,因此替换运算符只会删除文件开头后跟 . 的空格(包括换行符和制表符)#!。这-i.bak可确保您保留所有修改文件的备份,以防万一。如果您确定它按预期工作,则可以rm *.bak

这里使用的选项perl是:

  • -0:这将输入记录分隔符 ( $/) 指定为八进制或十六进制数。-0单独使用 an会使perl文件变得简单,并且基本上将其视为一行。 * -i.bak:编辑文件inplace,并使用扩展名创建原始文件的备份.bak
  • -p:逐行处理输入文件并在应用 给出的脚本后打印每一行-e
  • -e:传递要执行的脚本作为命令行参数。

答案2

perl -i -p -e 'if ($. == 1) {s/^\s+#!/#!/}' *

这将仅删除每个文件的#!第一行 ( )之前的前导空格。$. == 1所有其他线路均未经修改地通过。 perl无论是否发生任何更改,都会更新文件(即它们将有一个新的索引节点并且时间戳将被更新)。请参阅man perlrun并搜索第二次出现的 来-i\[了解详细信息)

如果您只想修改(更改时间戳、索引节点等)#! 之前有错误空格的文件,请尝试以下操作:

awk '/^[[:blank:]]+#!/ && FNR==1 { printf "%s\0", FILENAME }; {nextfile}' * |
    xargs -0r perl -i -p -e 'if ($. == 1) {s/^\s+#!/#!/}'

awk输出匹配文件的列表(第一行在 #! 之前有前导空格),由 NUL 分隔。这是为了在它们上xargs -0r运行单行代码而输入的。perl

nextfile功能需要 GNU awk。在其他版本中可以省略它,awk但运行速度会较慢(因为它必须读取每个文件的每一行,而不是在检查第一行后跳到下一个文件)。

这本来可以完全在中完成,perl但这需要更多的代码,而不仅仅是将awk的输出通过管道传输到xargs perl

相关内容