确定是否存在shebang的最快方法

Ale*_*lls 6 shell executable exec shebang

如果我有一个文件

#!/usr/bin/env foobar
Run Code Online (Sandbox Code Playgroud)

确定此文件是否具有 hashbang 的最快/最佳方法是什么?我听说你只能读取前 2 个字节?如何?

Sté*_*las 6

zsh

if LC_ALL=C read -u0 -k2 shebang < file && [ "$shebang" = '#!' ]; then
  echo has shebang
fi
Run Code Online (Sandbox Code Playgroud)

ksh93或相同bash

if IFS= LC_ALL=C read -rN2 shebang < file && [ "$shebang" = '#!' ]; then
  echo has shebang
fi
Run Code Online (Sandbox Code Playgroud)

虽然bash会给以NUL开头的文件误报,然后#!会读取所有前导 NUL 字节,因此会读取一个 1 tebibyte 的文件truncate -s1T file,例如一次创建完整的 2 个字节。

因此bash,最好使用:

IFS= LC_ALL=C read -rn2 -d '' shebang
Run Code Online (Sandbox Code Playgroud)

最多读取2 个字节的 NUL 分隔记录。

那些不会 fork 进程,也不会执行额外的命令,因为read,[并且echo命令都是内置的。

POSIXly,你可以这样做:

if IFS= read -r line < file; then
  case $line in
    ("#!"*) echo has shebang
  esac
fi
Run Code Online (Sandbox Code Playgroud)

它更严格,因为它也需要一个完整的行。至少在 Linux 上,有效的 shebang 不需要换行符。

所以你可以这样做:

line=
IFS= read -r line < file
case $line in
  ("#!"*) echo has shebang
esac
Run Code Online (Sandbox Code Playgroud)

它的效率稍低,因为它可能会读取更多字节,而某些 shell 一次读取一个字节。使用我们的 1TiB 稀疏文件,这在大多数 shell 中会花费很多时间(并且可能会使用大量内存)。

比其他炮弹zsh,也有可能产生假阳性的,与完全无效,随后启动文件#!

对于yashshell,如果 shebang 包含在当前语言环境中不构成有效字符的字节序列,它将失败(如果 shebang 在 C 语言环境中包含非 ASCII 字符,甚至会失败(至少在 2.39 及更早版本中),即使 C 语言环境意味着所有字符都是单个字节并且所有字节值都有效——即使不一定定义——字符)

如果要查找内容以 开头的所有文件#!,可以执行以下操作:

PERLIO=raw find . -type f -size +4c -exec perl -T -ne '
  BEGIN{$/=\2} print "$ARGV\n" if $_ eq "#!"; close ARGV' {} +
Run Code Online (Sandbox Code Playgroud)

我们只考虑至少 5 字节大的文件(#!/x\n最小的现实shebang)。

  • -exec perl... {} +,我们通过尽可能多的文件路径,以perl尽可能使运行尽可能少的调用尽可能
  • -T是为了解决 的限制,perl -n也意味着它不适用于名称以 ASCII 间距字符或|.
  • PERLIO=raw导致perlread()没有任何 IO 缓冲层的情况下直接使用系统调用(也会影响文件名的打印),因此它将执行大小为 2 的读取。
  • $/ = \2 当记录分隔符设置为对数字的引用时,它会导致记录为固定长度的记录。
  • close ARGV 在我们读取第一条记录后跳过当前文件的其余部分。


sag*_*aga 3

应该这样做:

if [ "`head -c 2 infile`" = "#!" ]; then
    echo "Hashbang present"
else
    echo "no Hashbang present"
fi
Run Code Online (Sandbox Code Playgroud)