Wie kann ich die wörtlichen Zeichen einer Bash-Befehlszeichenfolge prüfen?

1690
Gabriel Glenn

Ich hatte heute morgen dieses seltsame Verhalten in einem Bash-Terminal:

user@home:/home/user$ [ -f /etc/openvpn/client.conf ] && echo true bash: [: missing «]» user@home:/home/user$ [ -f /etc/openvpn/client.conf ] && echo true true 
  • Der erste Befehl wurde aus einem mit gedit bearbeiteten Skript eingefügt .
  • Der zweite wurde direkt im Terminal eingegeben .

Nach einigem Graben stelle ich fest, dass das Entfernen des 30. Zeichens (das Leerzeichen zwischen client.conf und "]") durch ein Leerzeichen ersetzt wurde, sodass der Befehl wieder funktioniert.

Meine Vermutung war richtig: Ein unbekanntes leeres Zeichen ist in den Befehl gerutscht, aber die Frage ist:

  1. Wie kann ich diese Zeichen im Terminal anzeigen, um den Befehl zu debuggen? Und noch wichtiger:
  2. Wie kann ich verhindern, dass dies erneut geschieht?

Übrigens, ich arbeite mit Ubuntu 18.04 / Französisch. Das Skript, aus dem ich den Befehl einfüge, befindet sich auf einem USB-Laufwerk und wurde möglicherweise auch unter Windows bearbeitet.


Vielen Dank für Ihre sehr guten Antworten. Das fehlerhafte Zeichen ist ein nicht-defektes Leerzeichen von c2 a0 als UTF-8-Zeichen. Die Frage, wie man einen speziellen 'M-BM-' Charakter mit sed entfernt, hat eine interessante Tatsache über diesen Charakter.

Das Merkwürdige ist, dass das Skript von diesem Charakter frei ist. Also weiß ich nicht woher es kam.

15
Verwenden Sie einen Editor, der solche Zeichen hervorhebt. Das Hervorheben von Syntax hilft auch sehr. Fügen Sie niemals direkt aus dem Internet in das Terminal ein, sondern führen Sie den oben genannten Editor aus. choroba vor 6 Jahren 3
Möglicherweise möchten Sie den Problembefehl in Ihrer Verlaufsliste finden und die Ausgabe dann über ein Hex-Anzeigeprogramm weiterleiten. Damit Sie keine langen Auflistungen durchgehen müssen, führen Sie entweder den Befehl erneut aus, um ihn am Ende der Verlaufsliste zu platzieren, und führen Sie `history 2 | xxd` aus (da der` history'-Befehl selbst immer der letzte in der Liste ist list) oder geben Sie `history | grep" CommandWithProblem "| xxd ein. Sie können anstelle von 'xxd' auch ein beliebiges anderes Hex-Anzeigeprogramm verwenden. Standardmäßig wird jedoch ein Format verwendet, das mir gefällt. AFH vor 6 Jahren 2
@Gabriel Glenn, bitte kreuzen Sie die beste / hilfreichste / egal welche Antwort als "_accepted_" an, indem Sie das Häkchen setzen - anstatt jeweils zu kommentieren, dass die Antwort hilfreich war. [info] (https://meta.stackexchange.com/a/5235/364309) Attie vor 6 Jahren 0
@Attie, ja das werde ich, ich warte normalerweise nur 24 Stunden, bevor ich die besten Antworten akzeptiere, wie in: https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work vorgeschlagen Gabriel Glenn vor 6 Jahren 1
Ich persönlich würde 'set -x' verwenden. Dies würde Ihnen den Befehl zeigen und wie er aufgeteilt wird. Es würde nicht unbedingt "schlechter Charakter hier" heißen, aber es würde Ihnen zeigen, dass Bash diesen Charakter nicht teilte. Patrick vor 6 Jahren 1
@Patrick Guter Vorschlag! Bitte fügen Sie das als Antwort hinzu. wjandrea vor 6 Jahren 0

4 Antworten auf die Frage

18
Kiwy

Sie könnten catmit der -AOption: aus dem Handbuch verwenden:

 -A, --show-all equivalent to -vET -E, --show-ends display $ at end of each line -T, --show-tabs display TAB characters as ^I -v, --show-nonprinting use ^ and M- notation, except for LFD and TAB 

So cat -A yourscrip.shzeigen Sie unsichtbare und fremde Charaktere.

Diese Lösung funktioniert: `echo" [-f /etc/openvpn.ovpn] "| cat -A` gibt `[-f /etc/openvpn/client.ovpnM-BM-] $` zurück. Wir sehen das * M-BM- * UT-8-Zeichen _nicht brechendes Leerzeichen_ Gabriel Glenn vor 6 Jahren 7
@GabrielGlenn freut sich, dass dir das geholfen hat. Kiwy vor 6 Jahren 0
11
Attie

Eine Möglichkeit besteht darin, sich die Zeichen anzusehen, die Sie mit einem Hex-Viewer oder -Editor verwenden möchten. hexdumpist eine gute Option, wenn Sie auf das Terminal beschränkt sind.

$ hexdump -Cv <<"EOF" > [ -f /etc/openvpn/client.conf ] && echo true > EOF 00000000 5b 20 2d 66 20 2f 65 74 63 2f 6f 70 65 6e 76 70 |[ -f /etc/openvp| 00000010 6e 2f 63 6c 69 65 6e 74 2e 63 6f 6e 66 20 5d 20 |n/client.conf ] | 00000020 26 26 20 65 63 68 6f 20 74 72 75 65 0a |&& echo true.| 0000002d 

Sie können hier sehen, dass die space, close-square-brace, spacekorrekt sind - 0x20, 0x5D, 0x20.

Diese Werte sind ASCII-Codes und werden hexadezimal angezeigt . Jeder Wert außerhalb des Bereichs 0x20- 0x7Eist für ASCII kein " druckbares Zeichen " und kann mit Befehlszeilenschnittstellen höchstwahrscheinlich nicht gut funktionieren.

Hinweis: Ich habe Ihre erste " unterbrochene " Zeile für die Verwendung im hexdumpobigen Beispiel kopiert, sodass etwas den Nicht-an-ASCII-Bereich durch einen ASCII-Bereich zwischen der ursprünglichen Quelle und Ihrer gerenderten Frage ersetzt hat.


Führen Sie die folgenden Schritte aus, um dies zu wiederholen:

  1. Geben Sie ein hexdump -Cv <<"EOF"und drücken SieEnter
  2. Fügen Sie den gewünschten Text ein
  3. Geben Sie EOFin eine eigene Zeile ein und drücken SieEnter

Terminals und Befehlszeilenschnittstellen behandeln Sonderzeichen nicht gut - wie Sie festgestellt haben. Wenn Sie beim Formatieren von Dokumenten nicht besonders vorsichtig sind, werden Sie auch Probleme mit Microsoft Word (und anderen) haben, wenn Sie " intelligente Anführungszeichen " verwenden, Bindestriche, die Liste wird fortgesetzt ...

Finde den Unterschied: (oben ist " intelligente Anführungszeichen ", unten sind " gerade Anführungszeichen ")

Beispiel für intelligente Anführungszeichen vs. gerade Anführungszeichen

$ hexdump -Cv <<"EOF" > “quoted string” > EOF 00000000 e2 80 9c 71 75 6f 74 65 64 20 73 74 72 69 6e 67 |...quoted string| 00000010 e2 80 9d 0a |....| 00000014 

Hier sind die offenen Angebote sind keine einfache ASCII - Zitat ( "), aber sind eine Unicode / UTF-8 - Serie - 0xE2, 0x80, 0x9Coder U+201C- was das Terminal nicht behandeln, wie man erwarten könnte.

Kiwys Vorschlag von cat -Amacht auch die Arbeit:

$ cat -A <<"EOF" > “quoted string” > EOF M-bM-^@M-^\quoted stringM-bM-^@M-^]$ 

Hinweis: Bei der Verwendungecho "..." | hdhaben Sie die Möglichkeit, dass bash Teile der Zeichenfolge ersetzt, die Sie untersuchen möchten. Dies ist besonders problematisch, wenn Sie versuchen, die Komponenten eines Skripts zu überprüfen.

Zum Beispiel versuchen Sie:

$ echo "$" attie  $ echo "`whoami`" attie  $ echo "$(whoami)" attie  $ cat <<EOF > $ > EOF attie 

Diese Methoden ersetzen Komponenten durch den entsprechenden Text. Um dies zu vermeiden, verwenden Sie einen der folgenden Ansätze. Beachten Sie die Verwendung von einfachen Anführungszeichen ( ') und einem " quoted heredoc " ( "EOF").

$ echo '$' $  $ echo '`whoami`' `whoami`  $ echo '$(whoami)' $(whoami)  $ cat <<"EOF" > $ > EOF $ 
Diese Lösung funktioniert: `echo" [-f /etc/openvpn.ovpn] "| hd `gibt` [...] c2 a0 [...] `zurück. Wir können das * c2 a0 * UT-8-Zeichen _non-breaking space_ sehen Gabriel Glenn vor 6 Jahren 0
9
xenoid

echo "<your command>" | hdsollte arbeiten. Suchen Sie nach Rückschritt (0x08) oder Zeichen mit Codes> = 80. echo "<your command>" | wc -bund zu überprüfen, ob die Zählung mit dem übereinstimmt, was Sie sehen, ist auch eine gute Idee.

Das Kopieren von Dingen aus Dateien, die mit "Office" im Namen erstellt wurden, ist gefährlich, da diese Software häufig die Erlaubnis übernimmt, Zeichen zu ersetzen: Auf Französisch sollten Sie nach Anführungszeichen, die durch "guillemets" ersetzt werden, Ausschau halten, in Englisch nach Anführungszeichen, die durch ihre Anführungszeichen ersetzt werden Äquivalente öffnen / schließen. Der schwierigste, den ich je gefunden habe, war ein sicherer Bereich mit einer Breite von 0 Breite in der Mitte eines Dateinamens (3 Tage Serverausfallzeit ...).

Erwähnenswert ist, dass "hd" kurz für "hexdump" steht, was auch in Atties Antwort erwähnt wird. Mikael Kjær vor 6 Jahren 2
@ MikaelKjær - Auf Ubuntu entspricht "hd" "hexdump -C". AFH vor 6 Jahren 0
@xenoid: Ich sagte 'bearbeitet unter Windows', nicht mit einem Office Writer bearbeitet, wir sind nicht verrückt;). Wenn es bearbeitet wurde, war es mit Notepad ++. Gabriel Glenn vor 6 Jahren 1
Diese Lösung funktioniert: `echo" [-f /etc/openvpn.ovpn] "| hd `gibt` [...] c2 a0 [...] `zurück. Wir können das * c2 a0 * UT-8-Zeichen _non-breaking space_ sehen Gabriel Glenn vor 6 Jahren 1
2
muru

Bash und andere Shells wie zsh können die aktuelle Befehlszeile in einem Editor öffnen. Die Standard - Tastenkürzel für bash ist C-x C-e( CtrlX CtrlE), und es öffnet sich in der ersten sich bietenden von $VISUAL, $EDITORund Emacs. In der Praxis ist dies für das Debuggen und Ändern komplexer Befehle von unschätzbarem Wert. Je nachdem, wie Sie es betrachten, ist zsh hier freundlicher als bash: Wenn der Editor beendet wird, führt bash den Befehl sofort aus, während zsh darauf wartet, dass Sie drücken, Enterwodurch Sie mehr Möglichkeiten haben, den Befehl zu bearbeiten.

Nachdem Sie den Befehl in einem Editor geöffnet haben, können Sie Ihre Editoren so konfigurieren, dass Nicht-ASCII-Zeichen unterschiedlich angezeigt werden.

Verwenden Sie zum Beispiel mit Vim diese Einstellungen:

set encoding=latin1 set isprint= set display+=uhex 

enter image description here

Oder die Methoden der anderen Antworten anpassen:

bash-4.4$ f() { cat -A "$@"; false; } # exit false to prevent bash from running the command bash-4.4$ VISUAL=f bash-4.4$ [ -f /etc/openvpn/client.conf ] && echo true # C-x C-e here [ -f /etc/openvpn/client.confM-BM- ] && echo true$