シェバンが存在するかどうかを判断する最も早い方法

シェバンが存在するかどうかを判断する最も早い方法

もしファイルがあったら

#!/usr/bin/env foobar

このファイルにハッシュバンがあるかどうかを判断する最も速くて最良の方法は何ですか? 最初の 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

ただし、bashNULで始まり、その後に続くファイルに対しては誤検出が発生し#!全てtruncate -s1T file先頭の NUL バイトは、たとえば 2 バイトで作成された 1 テビバイトのファイルを一度に読み取ることになります。

したがってbash、 の場合は、次のように使用する方がよいでしょう。

IFS= LC_ALL=C read -rn2 -d '' shebang

それは読むまでNUL で区切られたレコードの 2 バイト。

これらはプロセスをフォークしたり、 のように追加のコマンドを実行したりせずread、コマンド[echoすべて組み込まれています。

POSIX では、次のことができます。

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

完全な行も必要となるため、より厳密です。ただし、少なくとも Linux では、有効なシェバンには改行は必要ありません。

つまり、次のようにすることができます:

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

一部のシェルでは 1 バイトずつ読み取るため、より多くのバイトを読み取る可能性があるという点で、効率はわずかに低くなります。1TiB のスパース ファイルの場合、ほとんどのシェルでかなりの時間がかかります (また、大量のメモリを使用する可能性もあります)。

以外のシェルではzsh、NUL で始まり、その後に が続くファイルに対して誤検知が発生する可能性もあります#!

シェルではyash、シェバンに現在のロケールで有効な文字を形成しないバイトシーケンスが含まれている場合、失敗します (C ロケールでは、すべての文字が 1 バイトで、すべてのバイト値が有効な (必ずしも定義されていない場合でも) 文字を形成することを意図しているにもかかわらず、シェバンに C ロケールで非 ASCII 文字が含まれている場合も失敗します (少なくとも 2.39 以前では))。

内容が で始まるすべてのファイルを検索する場合は#!、次のようにします。

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

少なくとも 5 バイト (#!/x\n現実的な最小のシェバン) の大きさのファイルのみを検討します。

  • では-exec perl... {} +、できるだけ多くのファイルパスを に渡し、perlできるだけ少ない呼び出しを実行します。
  • -T回避することですその制限perl -nまた、名前が ASCII スペース文字または で終わるファイルでは機能しません|
  • PERLIO=rawperlIO バッファリング層なしでシステム コールを直接使用するようにしread()(ファイル名の印刷にも影響します)、サイズ 2 の読み取りを実行します。
  • $/ = \2レコード区切り文字を数値への参照として設定すると、レコードは固定長になります。
  • close ARGV最初のレコードを読み取った後、現在のファイルの残りをスキップします。

答え2

独自の「マジック パターン」を定義して/etc/magicfileテストに使用できます。

$ 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'#!' の逆順の16進数です:

$ 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 fileman 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

関連情報