Schnellste Methode, um festzustellen, ob Shebang vorhanden ist

Schnellste Methode, um festzustellen, ob Shebang vorhanden ist

Wenn ich eine Datei mit

#!/usr/bin/env foobar

Was ist der schnellste/beste Weg, um festzustellen, ob diese Datei einen Hashbang hat? Ich habe gehört, man kann einfach die ersten 2 Bytes lesen. Wie?

Antwort1

Mit zsh:

if LC_ALL=C read -u0 -k2 shebang < file && [ "$shebang" = '#!' ]; then
  echo has shebang
fi

Dasselbe mit ksh93oder bash:

if IFS= LC_ALL=C read -rN2 shebang < file && [ "$shebang" = '#!' ]; then
  echo has shebang
fi

würde jedoch bashfalsche Positivwerte für Dateien liefern, die mit NULs beginnen, gefolgt von #!und würde lautenalletruncate -s1T filedie führenden NUL-Bytes, so dass beispielsweise eine mit 2 gesamten Bytes erstellte ein Tebibyte-Datei auf einmal gelesen werden würde .

Daher bashwäre es bei besser, Folgendes zu verwenden:

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

Das wird gelesenbis zu2 Bytes eines NUL-getrennten Datensatzes.

Diese verzweigen weder Prozesse noch führen sie zusätzliche Befehle aus read, da die [-Befehle echoalle integriert sind.

POSIXly, Sie können Folgendes tun:

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

Es ist strenger, da es auch eine vollständige Zeile erfordert. Zumindest unter Linux ist die neue Zeile für einen gültigen Shebang jedoch nicht erforderlich.

Sie könnten also Folgendes tun:

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

Es ist etwas weniger effizient, da es möglicherweise mehr Bytes lesen würde, bei einigen Shells ein Byte nach dem anderen. Bei unserer 1-TiB-Sparse-Datei würde das bei den meisten Shells viel Zeit in Anspruch nehmen (und möglicherweise viel Speicher verbrauchen).

Mit anderen Shells als zshkann es auch zu Fehlalarmen für Dateien kommen, die mit NULs beginnen, gefolgt von #!.

Mit der yashShell würde es fehlschlagen, wenn das Shebang Bytefolgen enthält, die im aktuellen Gebietsschema keine gültigen Zeichen bilden (würde sogar fehlschlagen (zumindest mit 2.39 und älter), wenn das Shebang im Gebietsschema C nicht-ASCII-Zeichen enthielte, obwohl das Gebietsschema C dasjenige sein soll, in dem alle Zeichen einzelne Bytes sind und alle Bytewerte gültige – auch wenn nicht notwendigerweise definierte – Zeichen bilden).

Wenn Sie alle Dateien finden möchten, deren Inhalt mit beginnt #!, können Sie Folgendes tun:

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

Wir berücksichtigen nur Dateien mit einer Größe von mindestens 5 Byte ( #!/x\ndas realistische Minimum).

  • mit -exec perl... {} +übergeben wir so viele Dateipfade perlwie möglich, um so wenig Aufrufe wie möglich auszuführen
  • -Tist zu umgehendiese Beschränkung derperl -nund bedeutet auch, dass es nicht für Dateien funktioniert, deren Name mit ASCII-Leerzeichen oder endet |.
  • PERLIO=rawbewirkt , dass Systemaufrufe direkt ohne eine IO-Pufferschicht perlverwendet werden read()(wirkt sich auch auf das Drucken von Dateinamen aus), sodass Lesevorgänge der Größe 2 ausgeführt werden.
  • $/ = \2Wenn der Datensatztrenner als Verweis auf eine Zahl festgelegt wird, hat dies zur Folge, dass die Datensätze Datensätze mit fester Länge sind.
  • close ARGVüberspringt den Rest der aktuellen Datei, nachdem wir den ersten Datensatz gelesen haben.

Antwort2

Sie können Ihre eigenen „magischen Muster“ definieren und zum Testen /etc/magicverwenden :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

0x2123ist Hex von „#!“ in umgekehrter Reihenfolge:

$ 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 `!'

Optional können Sie Folgendes eingeben:

0 string \#\! shebang is present

Referenz: man 5 magic, man 1 file, man 1posix file

Antwort3

Das sollte es tun:

if [ "`head -c 2 infile`" = "#!" ]; then
    echo "Hashbang present"
else
    echo "no Hashbang present"
fi

Antwort4

Verwendung grepin einer Einzeilerlösung

if head -1 file | grep "^#\!" > /dev/null;then echo "true"; fi

verwandte Informationen