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 ksh93
oder bash
:
if IFS= LC_ALL=C read -rN2 shebang < file && [ "$shebang" = '#!' ]; then
echo has shebang
fi
würde jedoch bash
falsche Positivwerte für Dateien liefern, die mit NULs beginnen, gefolgt von #!
und würde lautenalletruncate -s1T file
die führenden NUL-Bytes, so dass beispielsweise eine mit 2 gesamten Bytes erstellte ein Tebibyte-Datei auf einmal gelesen werden würde .
Daher bash
wä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 echo
alle 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 zsh
kann es auch zu Fehlalarmen für Dateien kommen, die mit NULs beginnen, gefolgt von #!
.
Mit der yash
Shell 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\n
das realistische Minimum).
- mit
-exec perl... {} +
übergeben wir so viele Dateipfadeperl
wie möglich, um so wenig Aufrufe wie möglich auszuführen -T
ist zu umgehendiese Beschränkung derperl -n
und bedeutet auch, dass es nicht für Dateien funktioniert, deren Name mit ASCII-Leerzeichen oder endet|
.PERLIO=raw
bewirkt , dass Systemaufrufe direkt ohne eine IO-Pufferschichtperl
verwendet werdenread()
(wirkt sich auch auf das Drucken von Dateinamen aus), sodass Lesevorgänge der Größe 2 ausgeführt werden.$/ = \2
Wenn 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/magic
verwenden :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
ist 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 grep
in einer Einzeilerlösung
if head -1 file | grep "^#\!" > /dev/null;then echo "true"; fi