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

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

如果我有一个文件

#!/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 开头、后跟#!.

对于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' {} +

我们只考虑至少 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

相关内容