我在一个地方收集了大量非二进制文件。其中一些有 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
:编辑文件i
nplace,并使用扩展名创建原始文件的备份.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