Ale*_*lls 6 shell executable exec shebang
如果我有一个文件
#!/usr/bin/env foobar
Run Code Online (Sandbox Code Playgroud)
确定此文件是否具有 hashbang 的最快/最佳方法是什么?我听说你只能读取前 2 个字节?如何?
与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
,也有可能产生假阳性的,与完全无效,随后启动文件#!
。
对于yash
shell,如果 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
导致perl
在read()
没有任何 IO 缓冲层的情况下直接使用系统调用(也会影响文件名的打印),因此它将执行大小为 2 的读取。$/ = \2
当记录分隔符设置为对数字的引用时,它会导致记录为固定长度的记录。close ARGV
在我们读取第一条记录后跳过当前文件的其余部分。应该这样做:
if [ "`head -c 2 infile`" = "#!" ]; then
echo "Hashbang present"
else
echo "no Hashbang present"
fi
Run Code Online (Sandbox Code Playgroud)