Ich versuche ehrgeizig, einen C ++ - Code aus einer Vielzahl von Gründen in Bash zu übersetzen.
Dieser Code liest und manipuliert einen für mein Unterfeld spezifischen Dateityp, der vollständig binär geschrieben und strukturiert ist. Meine erste Aufgabe im Zusammenhang mit Binärdateien besteht darin, die ersten 988 Bytes des Headers so wie sie sind zu kopieren und sie in eine Ausgabedatei zu schreiben, in die ich weiter schreiben kann, während ich den Rest der Informationen generiere.
Ich bin mir ziemlich sicher, dass meine derzeitige Lösung nicht funktioniert, und realistisch gesehen habe ich keinen guten Weg gefunden, dies festzustellen. Selbst wenn es richtig geschrieben ist, muss ich wissen, wie ich das testen würde, um sicherzugehen!
Das mache ich gerade:
hdr_988=`head -c 988 ${inputFile}`
echo -n "${hdr_988}" > ${output_hdr}
headInput=`head -c 988 ${inputTrack} | hexdump`
headOutput=`head -c 988 ${output_hdr} | hexdump`
if [ "${headInput}" != "${headOutput}" ]; then echo "output header was not written properly. exiting. please troubleshoot."; exit 1; fi
Wenn ich diesen Teil der Datei mit hexdump / xxd auschecke, obwohl ich das meiste nicht genau lesen kann, scheint etwas nicht in Ordnung zu sein. Und der Code, den ich zum Vergleich geschrieben habe, sagt mir nur, ob zwei Zeichenfolgen identisch sind, nicht, ob sie so kopiert werden, wie ich sie haben möchte.
Gibt es eine bessere Möglichkeit, dies in Bash zu tun? Kann ich einfach binäre Bytes in native-binary kopieren / lesen, um sie wörtlich in eine Datei zu kopieren? (und idealerweise auch als Variable speichern).
dd
einzelnen Bytes kopieren (seine Einstellungcount
zu1
). Ich bin mir jedoch nicht sicher, ob ich sie aufbewahren soll.Antworten:
Der Umgang mit Binärdaten auf einer niedrigen Ebene in Shell-Skripten ist im Allgemeinen eine schlechte Idee.
bash
Variablen dürfen das Byte 0 nicht enthalten. Dieszsh
ist die einzige Shell, die dieses Byte in ihren Variablen speichern kann.In jedem Fall dürfen Befehlsargumente und Umgebungsvariablen diese Bytes nicht enthalten, da es sich um durch NUL getrennte Zeichenfolgen handelt, die an den
execve
Systemaufruf übergeben werden.Beachten Sie auch, dass:
oder seine moderne Form:
entfernt alle nachfolgenden Zeilenumbrüche aus der Ausgabe von
cmd
. Also, wenn das binäre Ausgangsenden in 0xa Bytes, wird sie verstümmelt werden , wenn in gespeichert$var
.Hier müssten Sie die Daten verschlüsselt speichern, zum Beispiel mit
xxd -p
.Sie können Hilfsfunktionen definieren wie:
xxd -p
Die Ausgabe ist nicht platzsparend, da sie 1 Byte in 2 Bytes codiert. Sie erleichtert jedoch die Manipulationen (Verketten, Extrahieren von Teilen).base64
ist eine, die 3 Bytes in 4 codiert, aber nicht so einfach zu bearbeiten ist.Die
ksh93
Shell hat ein eingebautes Codierungsformat (verwendetbase64
), das Sie mit den Dienstprogrammenread
undprintf
/ verwenden könnenprint
:Wenn jetzt keine Übertragung über Shell- oder Umgebungsvariablen oder Befehlsargumente erfolgt, sollten Sie in Ordnung sein, solange die von Ihnen verwendeten Dienstprogramme einen beliebigen Bytewert verarbeiten können. Beachten Sie jedoch, dass für Textdienstprogramme die meisten Nicht-GNU-Implementierungen NUL-Bytes nicht verarbeiten können und Sie das Gebietsschema auf C korrigieren möchten, um Probleme mit Mehrbyte-Zeichen zu vermeiden. Das letzte Zeichen, das kein Zeilenvorschubzeichen ist, kann auch Probleme sowie sehr lange Zeilen verursachen (Folgen von Bytes zwischen zwei 0xa-Bytes, die länger sind
LINE_MAX
).head -c
wo es verfügbar ist, sollte hier OK sein, da es mit Bytes arbeiten soll und keinen Grund hat, die Daten als Text zu behandeln. Sosollte in Ordnung sein. In der Praxis sind zumindest die in GNU, FreeBSD und ksh93 eingebauten Implementierungen in Ordnung. POSIX gibt die
-c
Option nicht an, sagt jedoch, dasshead
Zeilen beliebiger Länge unterstützt werden sollen (nicht beschränkt aufLINE_MAX
).Mit
zsh
:Oder:
Selbst
zsh
wenn$var
NUL-Bytes enthalten sind, können Sie diese als Argumente anzsh
Builtins (wieprint
oben) oder Funktionen übergeben, nicht jedoch als Argumente an ausführbare Dateien. Argumente, die an ausführbare Dateien übergeben werden, sind durch NUL getrennte Zeichenfolgen. Dies ist eine Kernel-Einschränkung, unabhängig von der Shell.quelle
zsh
ist nicht die einzige Shell, die ein oder mehrere NUL-Bytes in einer Shell-Variablen speichern kann.ksh93
kann das auch. Intern wirdksh93
die Binärvariable einfach als base64-codierte Zeichenfolge gespeichert.Nun ja. Aber vielleicht sollten Sie einen sehr wichtigen Grund in Betracht ziehen, es NICHT zu tun. Grundsätzlich sind "bash" / "sh" / "csh" / "ksh" und dergleichen nicht für die Verarbeitung von Binärdaten vorgesehen, und die meisten Standard-UNIX / LINUX-Dienstprogramme sind dies auch nicht.
Sie sollten sich lieber an C ++ halten oder Skriptsprachen wie Python, Ruby oder Perl verwenden, die mit Binärdaten umgehen können.
Der bessere Weg ist, es nicht in bash zu tun.
quelle
ffmpeg
,imagemagick
,dd
). Wenn man programmiert, anstatt Dinge zusammenzukleben, ist die Verwendung einer vollwertigen Programmiersprache der richtige Weg.Aus Ihrer Frage:
Wenn Sie 988 Zeilen kopieren, scheint es sich um eine Textdatei zu handeln, nicht um eine Binärdatei. Ihr Code scheint jedoch 988 Bytes und keine 988 Zeilen anzunehmen, daher gehe ich davon aus, dass Bytes korrekt sind.
Dieser Teil funktioniert möglicherweise nicht. Zum einen werden alle NUL-Bytes im Stream entfernt, da Sie sie
${hdr_988}
als Befehlszeilenargument verwenden und Befehlszeilenargumente nicht NUL enthalten können. Die Backticks tun möglicherweise auch Whitespace-Munging (da bin ich mir nicht sicher). (Daecho
es sich um eine integrierte Funktion handelt, gilt die NUL-Einschränkung möglicherweise nicht, aber ich würde sagen, dass sie immer noch zweifelhaft ist.)Warum nicht einfach den Header direkt aus der Eingabedatei in die Ausgabedatei schreiben, ohne ihn über eine Shell-Variable zu übergeben?
Oder tragbarer,
Da Sie erwähnen, dass Sie die
bash
POSIX-Shell und nicht die POSIX-Shell verwenden, steht Ihnen eine Prozessersetzung zur Verfügung. Wie wäre es also als Test?Schließlich: Sehen Sie verwenden
$( ... )
anstelle von Backticks.quelle
dd
nicht unbedingthead
für nicht reguläre Dateien gleichbedeutend ist .head
führt so vieleread(2)
Systemaufrufe durch, wie erforderlich sind, um diese 988 Bytes zu erhalten, währenddd
nur einer ausgeführt wirdread(2)
. GNUdd
mussiflag=fullblock
versuchen, diesen Block vollständig zu lesen, aber das ist dann noch weniger portabel alshead -c
.