bash kann den Hex-Wert 0x00 nicht in einer Variablen speichern

11

Ich versuche ein paar Tricks mit dd zu machen. Ich dachte, es wäre möglich, einige Hex-Werte in einer Variablen namens "header" zu speichern, um sie in dd zu leiten.

Mein erster Schritt ohne Variable war folgender:

$ echo -ne "\x36\xc9\xda\x00\xb4" |dd of=hex
$ hd hex

00000000  36 c9 da 00 b4                                    |6....|
00000005

Danach habe ich folgendes versucht:

$ header=$(echo -ne "\x36\xc9\xda\x00\xb4") 
$ echo -n $header | hd

00000000  36 c9 da b4                                       |6...|
00000004

Wie Sie sehen, habe ich meinen \x00Wert in der $headerVariablen verloren. Hat jemand eine Erklärung für dieses Verhalten? Das macht mich verrückt.

Frank
quelle
Ich verstehe bash: warning: command substitution: ignored null byte in input.
Kusalananda
Ihnen fehlen Anführungszeichen, es sollte header="$(echo -ne "\x36\xc9\xda\x00\xb4")"; echo -n "$header" | hdjedoch sein, dass dies nur das gleiche Ergebnis liefert.
Strg-Alt-Delor
Dies funktioniert header="\x36\xc9\xda\x00\xb4"; echo -n "$header" | hd, ist aber nicht dasselbe wie das Speichern der vom Menschen lesbaren Form.
Strg-Alt-Delor

Antworten:

16

Sie können kein Null-Byte in einer Zeichenfolge speichern, da Bash Zeichenfolgen im C-Stil verwendet, die das Null-Byte für Terminatoren reservieren. Sie müssen Ihr Skript also neu schreiben, um einfach die Sequenz weiterzuleiten, die das Null-Byte enthält, ohne dass Bash es in der Mitte speichern muss. Zum Beispiel können Sie dies tun:

printf "\x36\xc9\xda\x00\xb4" | hd

Beachten Sie übrigens, dass Sie nicht brauchen echo; Sie können Bash's printffür viele andere einfache Aufgaben verwenden.

Oder anstatt zu verketten, können Sie eine temporäre Datei verwenden:

printf "\x36\xc9\xda\x00\xb4" > /tmp/mysequence
hd /tmp/mysequence

Dies hat natürlich das Problem, dass die Datei /tmp/mysequencemöglicherweise bereits vorhanden ist. Und jetzt müssen Sie weiterhin temporäre Dateien erstellen und deren Pfade in Zeichenfolgen speichern.

Oder Sie können dies vermeiden, indem Sie die Prozessersetzung verwenden:

hd <(printf "\x36\xc9\xda\x00\xb4")

Der <(command)Operator erstellt im Dateisystem eine Named Pipe, die die Ausgabe von empfängt command. hderhält als erstes Argument den Pfad zu dieser Pipe, die fast wie jede Datei geöffnet und gelesen wird . Weitere Informationen finden Sie hier: /unix//a/17117/136742 .

Giusti
quelle
1
Dies ist zwar korrekt, aber ein Implementierungsdetail und nicht der genaue Grund. Ich habe es mir angesehen, und der POSIX-Standard erfordert dieses Verhalten tatsächlich , sodass Sie den eigentlichen Grund haben. (Wie einige darauf hingewiesen haben, zshwird es tun, aber nur im Nōn-POSIX-Modus.) Ich habe es tatsächlich untersucht, weil ich mich gefragt habe, ob es sich lohnt, dies in mksh
mirabilos
@mirabilos, möchten Sie das erweitern? AFAICT, das Verhalten ist pro POSIX für die Befehlssubstitution nicht angegeben, wenn die Ausgabe NUL-Zeichen enthält, und für zsh im POSIX-Modus ist der einzige relevante Unterschied, den ich mir vorstellen kann, dass der in der shEmulation \0nicht im Standardwert von liegt $IFS. echo "$(printf 'a\0b')"funktioniert immer noch OK in der shEmulation in zsh.
Stéphane Chazelas
3
@mirabilos Bedenkt man, dass die Schalen der POSIX - Standard von einem Jahrzehnt oder mehr älter, ich denke , Sie herausfinden können , dass die tatsächlichen Ist - Grund ist , dass Schalen C-Strings verwendet und die Standard wurde um das gebaut.
Giusti
Ich fand ein gutes Q für eine ausführliche Diskussion über printf versus echo. unix.stackexchange.com/questions/65803/…
Paulb
8

Sie können zshstattdessen die einzige Shell verwenden, die das NUL-Zeichen in seinen Variablen speichern kann. Dieses Zeichen hat sogar den Standardwert von $IFSin zsh.

nul=$'\0'

Oder:

nul=$'\x0'

Oder

nul=$'\u0000'

Oder

nul=$(printf '\0')

Beachten Sie jedoch, dass Sie eine solche Variable nicht als Argument oder Umgebungsvariable an einen Befehl übergeben können, der ausgeführt wird, da die Argumente und Umgebungsvariablen NUL-getrennte Zeichenfolgen sind, die an den execve()Systemaufruf übergeben werden (eine Einschränkung der API des Systems, nicht der Shell ). In zshkönnen Sie jedoch NUL-Bytes als Argumente an Funktionen oder integrierte Befehle übergeben.

echo $'\0' # works
/bin/echo $'\0' # doesn't
Stéphane Chazelas
quelle
1
"Sie können stattdessen zsh verwenden". Nein danke - ich bringe mir gerade als Anfänger das Bash-Scripting bei. Ich möchte mich nicht mit einer anderen Syntax verwechseln. Aber vielen Dank für den Vorschlag
Frank
Tatsächlich haben Sie zshin Ihrer Frage die Syntax verwendet . echo -n $headerzu bedeuten , den Inhalt der zu übergeben $headerVariable als letztes Argument echo -nist zsh(oder fishoder rcoder es) Syntax, nicht - bash Syntax. In bashhat das eine ganz andere Bedeutung . Im Allgemeinen zshist es wie ksh( bashdie GNU-Shell ist mehr oder weniger ein Teilklon kshder Unix-De-facto-Shell), aber mit den meisten Design-Besonderheiten der Bourne-Shell (und vielen zusätzlichen Funktionen und vielem mehr) benutzerfreundlicher / weniger erstaunlich).
Stéphane Chazelas
Seien Sie vorsichtig: zsh kann manchmal ein Null-Byte ändern: echo $(printf 'ab\0cd') | od -vAn -tx1c Gibt `61 62 20 63 64 0a` aus, das ist ein Raum, in dem ein NUL existieren sollte.
Sorontar
1
Und das ist etwas, das keine andere (keine, keine) Schale reproduzieren wird. Dadurch verhält sich ein Skript in zsh auf ganz besondere Weise. Meiner Meinung nach versucht zsh nur, zu schlau zu sein.
Sorontar
2
Wenn man die im POSIX sh-Standard vorhandenen Designfehler "behoben" hat, bedeutet dies, dass man sich an das Schreiben von zsh-Skripten gewöhnt, dass man sich an Praktiken gewöhnt, die fehlerhaft wären, wenn sie in einer anderen Shell ausgeführt würden. Dies ist kein Problem mit einer Syntax, die einer anderen Sprache so unähnlich ist, dass Fähigkeiten oder Gewohnheiten wahrscheinlich nicht übertragen werden, aber dies ist derzeit nicht der Fall.
Charles Duffy