如果我有一个文件
#!/usr/bin/env foobar
确定该文件是否有 hashbang 的最快/最好的方法是什么?我听说你只能读取前 2 个字节?如何?
答案1
和zsh
:
if LC_ALL=C read -u0 -k2 shebang < file && [ "$shebang" = '#!' ]; then
echo has shebang
fi
与ksh93
或相同bash
:
if IFS= LC_ALL=C read -rN2 shebang < file && [ "$shebang" = '#!' ]; then
echo has shebang
fi
但对于以 NUL 开头、后跟和 的bash
文件会产生误报#!
全部truncate -s1T file
例如,前导 NUL 字节将读取一次使用 2 个字节创建的 1 tebibyte 文件。
因此bash
,使用 ,最好使用:
IFS= LC_ALL=C read -rn2 -d '' shebang
也就是读到的取决于2 个字节的 NUL 分隔记录。
这些不会分叉进程,也不会执行额外的命令,因为read
,[
和echo
命令都是内置的。
POSIXly,你可以这样做:
if IFS= read -r line < file; then
case $line in
("#!"*) echo has shebang
esac
fi
它更严格,因为它还要求完整的线路。至少在 Linux 上,有效的 shebang 不需要换行符。
所以你可以这样做:
line=
IFS= read -r line < file
case $line in
("#!"*) echo has shebang
esac
它的效率稍低,因为它可能会读取更多字节,而某些 shell 一次只能读取一个字节。对于我们的 1TiB 稀疏文件,在大多数 shell 中这将花费大量时间(并且可能使用大量内存)。
对于除此之外的 shell zsh
,它也可能会对以 NUL 开头、后跟#!
.
对于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' {} +
我们只考虑至少 5 个字节大的文件(#!/x\n
最小的现实 shebang)。
- 使用
-exec perl... {} +
,我们传递尽可能多的文件路径,perl
因此运行尽可能少的调用 -T
是要解决该限制perl -n
并且还意味着它不适用于名称以 ASCII 空格字符或|
.PERLIO=raw
导致直接perl
使用read()
系统调用而无需任何 IO 缓冲层(也会影响文件名的打印),因此它将执行大小为 2 的读取。$/ = \2
当记录分隔符设置为对数字的引用时,它会导致记录成为固定长度的记录。close ARGV
读取第一条记录后跳过当前文件的其余部分。
答案2
您可以定义自己的“魔术模式”/etc/magic
并用于file
测试:
$ sudo vi /etc/magic
$ cat /etc/magic
# Magic local data for file(1) command.
# Insert here your local magic data. Format is described in magic(5).
0 byte 0x2123 shebang is present
$ cat /tmp/hole2.sh #To prove [1] order of hex [2] 2nd line ignored
!#/bin/bash
#!/bin/bash
$ cat /tmp/hole.sh
#!/bin/bash
$ file /tmp/hole2.sh
/tmp/hole2.sh: ASCII text
$ file /tmp/hole.sh
/tmp/hole.sh: shebang is present
$ file -b /tmp/hole.sh #omit filename
shebang is present
0x2123
是十六进制的“#!”按相反顺序:
$ ascii '#' | head -n1
ASCII 2/3 is decimal 035, hex 23, octal 043, bits 00100011: prints as `#'
$ ascii '!' | head -n1
ASCII 2/1 is decimal 033, hex 21, octal 041, bits 00100001: prints as `!'
您可以选择放置:
0 string \#\! shebang is present
参考:man 5 magic
, man 1 file
, man 1posix file
答案3
应该这样做:
if [ "`head -c 2 infile`" = "#!" ]; then
echo "Hashbang present"
else
echo "no Hashbang present"
fi
答案4
用于grep
单线解决方案
if head -1 file | grep "^#\!" > /dev/null;then echo "true"; fi