Für die neue Frage funktioniert dieses Skript:
#!/bin/bash
f() { for i in $(seq "$((RANDOM % 3 ))"); do
echo;
done; return $((RANDOM % 256));
}
exact_output(){ out=$( $1; ret=$?; echo x; exit "$ret" );
unset OldLC_ALL ; [ "${LC_ALL+set}" ] && OldLC_ALL=$LC_ALL
LC_ALL=C ; out=${out%x};
unset LC_ALL ; [ "${OldLC_ALL+set}" ] && LC_ALL=$OldLC_ALL
printf 'Output:%10q\nExit :%2s\n' "${out}" "$?"
}
exact_output f
echo Done
Bei der Ausführung:
Output:$'\n\n\n'
Exit :25
Done
Die längere Beschreibung
Die übliche Weisheit für POSIX-Shells, sich mit dem Entfernen von zu befassen, \n
ist:
füge ein x
s=$(printf "%s" "${1}x"); s=${s%?}
Das ist erforderlich , weil die letzte neue Linie ( S ) durch den Befehl Erweiterung entfernt werden pro POSIX - Spezifikation :
Entfernen von Sequenzen eines oder mehrerer Zeichen am Ende der Ersetzung.
Über ein Trailing x
.
Es wurde in dieser Frage gesagt, dass ein x
mit dem nachfolgenden Byte eines Zeichens in einer Codierung verwechselt werden könnte. Aber wie sollen wir raten, welches oder welches Zeichen in einer Sprache in einer möglichen Kodierung besser ist, das ist, gelinde gesagt, eine schwierige Aussage.
Jedoch; Das ist einfach falsch .
Die einzige Regel, der wir folgen müssen, ist, genau das hinzuzufügen , was wir entfernen.
Es sollte leicht zu verstehen sein , dass , wenn wir etwas zu einer vorhandenen Zeichenfolge (oder Byte - Reihenfolge) hinzufügen und später entfernen wir genau das gleiche , etwas, die ursprüngliche Zeichenfolge (oder Byte - Reihenfolge) müssen gleich sein.
Wohin gehen wir falsch? Wenn wir Zeichen und Bytes mischen .
Wenn wir ein Byte hinzufügen, müssen wir ein Byte entfernen. Wenn wir ein Zeichen hinzufügen, müssen wir genau dasselbe Zeichen entfernen .
Die zweite Option, ein Zeichen hinzuzufügen (und später genau dasselbe Zeichen zu entfernen), kann kompliziert und kompliziert werden, und Codepages und Codierungen können im Weg stehen.
Die erste Option ist jedoch durchaus möglich, und nachdem sie erklärt wurde, wird sie ganz einfach.
Fügen wir ein Byte, ein ASCII-Byte (<127) hinzu und lassen Sie uns ein ASCII-Zeichen im Bereich von az angeben, um die Verschachtelung so gering wie möglich zu halten. Oder wie wir es sagen sollten, ein Byte im Hex-Bereich 0x61
- 0x7a
. Wählen wir eines davon, vielleicht ein x (wirklich ein Byte Wert 0x78
). Wir können ein solches Byte hinzufügen, indem wir ein x mit einem String verketten (nehmen wir an, ein é
):
$ a=é
$ b=${a}x
Wenn wir die Zeichenfolge als Folge von Bytes betrachten, sehen wir:
$ printf '%s' "$b" | od -vAn -tx1c
c3 a9 78
303 251 x
Eine Zeichenfolge, die mit einem x endet.
Wenn wir dieses x (Byte-Wert 0x78
) entfernen , erhalten wir:
$ printf '%s' "${b%x}" | od -vAn -tx1c
c3 a9
303 251
Das funktioniert ohne Probleme.
Ein etwas schwierigeres Beispiel.
Nehmen wir an, die Zeichenfolge, an der wir interessiert sind, endet in Byte 0xc3
:
$ a=$'\x61\x20\x74\x65\x73\x74\x20\x73\x74\x72\x69\x6e\x67\x20\xc3'
Und lassen Sie uns ein Byte Wert hinzufügen 0xa9
$ b=$a$'\xa9'
Die Zeichenfolge ist jetzt so geworden:
$ echo "$b"
a test string é
Genau das, was ich wollte, die letzten zwei Bytes sind ein Zeichen in utf8 (damit jeder diese Ergebnisse in seiner utf8-Konsole reproduzieren kann).
Wenn wir ein Zeichen entfernen, wird die ursprüngliche Zeichenfolge geändert. Aber das haben wir nicht hinzugefügt, wir haben einen Bytewert hinzugefügt, der zufällig als x geschrieben wird, aber trotzdem als Byte.
Was wir brauchen, um eine Fehlinterpretation von Bytes als Zeichen zu vermeiden. Was wir brauchen, ist eine Aktion, die das verwendete Byte entfernt 0xa9
. Tatsächlich scheinen ash, bash, lksh und mksh genau das zu tun:
$ c=$'\xa9'
$ echo ${b%$c} | od -vAn -tx1c
61 20 74 65 73 74 20 73 74 72 69 6e 67 20 c3 0a
a t e s t s t r i n g 303 \n
Aber nicht ksh oder zsh.
Das ist jedoch sehr einfach zu lösen. Sagen wir all diesen Shells, dass sie das Entfernen von Bytes durchführen sollen:
$ LC_ALL=C; echo ${b%$c} | od -vAn -tx1c
Das war's, alle Shells getestet Arbeit (außer Yash) (für den letzten Teil der Zeichenfolge):
ash : s t r i n g 303 \n
dash : s t r i n g 303 \n
zsh/sh : s t r i n g 303 \n
b203sh : s t r i n g 303 \n
b204sh : s t r i n g 303 \n
b205sh : s t r i n g 303 \n
b30sh : s t r i n g 303 \n
b32sh : s t r i n g 303 \n
b41sh : s t r i n g 303 \n
b42sh : s t r i n g 303 \n
b43sh : s t r i n g 303 \n
b44sh : s t r i n g 303 \n
lksh : s t r i n g 303 \n
mksh : s t r i n g 303 \n
ksh93 : s t r i n g 303 \n
attsh : s t r i n g 303 \n
zsh/ksh : s t r i n g 303 \n
zsh : s t r i n g 303 \n
Weisen Sie die Shell einfach an, ein LC_ALL = C-Zeichen zu entfernen. Dies ist genau ein Byte für alle Bytewerte von 0x00
bis 0xff
.
Lösung für Kommentare:
Für das in den Kommentaren diskutierte Beispiel ist eine mögliche Lösung (die in zsh fehlschlägt):
#!/bin/bash
LC_ALL=zh_HK.big5hkscs
a=$(printf '\210\170');
b=$(printf '\170');
unset OldLC_ALL ; [ "${LC_ALL+set}" ] && OldLC_ALL=$LC_ALL
LC_ALL=C ; a=${a%"$b"};
unset LC_ALL ; [ "${OldLC_ALL+set}" ] && LC_ALL=$OldLC_ALL
printf '%s' "$a" | od -vAn -c
Dadurch wird das Codierungsproblem behoben.
$IFS
, daher wird es nicht als Argument erfasst.