Nur eine zusätzliche Anmerkung zu @ Kusalanandas guter Antwort .
echo run after_bundle
ist in Ordnung, da keines der Zeichen in diesen 3 Argumenten¹ an übergeben wurde echo
Zeichen enthält, die für die Shell speziell sind.
Und (der zusätzliche Punkt, den ich hier ansprechen möchte) es gibt kein Systemgebietsschema, in dem diese Bytes in Zeichen übersetzt werden könnten, die für die Shell speziell sind.
Alle diese Zeichen befinden sich in dem, was POSIX als tragbaren Zeichensatz bezeichnet . Diese Zeichen sollten in allen Zeichensätzen auf einem POSIX-System² vorhanden und gleich codiert sein.
Diese Befehlszeile wird also unabhängig vom Gebietsschema gleich interpretiert.
Wenn wir jetzt Zeichen außerhalb dieses tragbaren Zeichensatzes verwenden, ist es eine gute Idee, sie in Anführungszeichen zu setzen, auch wenn sie nicht speziell für die Shell sind, da in einem anderen Gebietsschema die Bytes, aus denen sie bestehen, möglicherweise als unterschiedliche Zeichen interpretiert werden speziell für die Shell. Beachten Sie echo
, dass das Problem nicht bei einem Befehl oder einem anderen Befehl liegtecho
sondern darin, wie die Shell ihren Code analysiert.
Zum Beispiel in einem UTF-8:
echo voilà | iconv -f UTF-8 -t //TRANSLIT
Das à
ist als 0xc3 0xa0 codiert. Wenn Sie diese Codezeile in einem Shell-Skript haben und das Shell-Skript von einem Benutzer aufgerufen wird, der ein Gebietsschema verwendet, dessen Zeichensatz nicht UTF-8 ist, können diese beiden Bytes sehr unterschiedliche Zeichen enthalten.
In einem fr_FR.ISO8859-15
Gebietsschema, einem typischen französischen Gebietsschema, das den Standard-Einzelbyte-Zeichensatz verwendet, der die französische Sprache abdeckt (dasselbe gilt für die meisten westeuropäischen Sprachen einschließlich Englisch), wird dieses 0xc3-Byte als das interpretiertÃ
Zeichen und 0xa0 als Nicht- Breaking Space Charakter.
Und auf einigen Systemen wie NetBSD³ wird dieses nicht unterbrechende Leerzeichen als leeres Zeichen betrachtet ( isblank()
auf dem true zurückgegeben wird, mit dem es übereinstimmt [[:blank:]]
), und Shells wie bash
behandeln es daher als Token-Trennzeichen in ihrer Syntax.
Das bedeutet , dass anstelle des Laufens echo
mit $'voil\xc3\xa0'
als Argument, laufen sie es mit $'voil\xc3'
als Argument, was bedeutet , es nicht gedruckt wird , voilà
korrekt.
Es wird noch viel schlimmer mit chinesischen Zeichensätzen wie BIG5, BIG5-HKSCS, GB18030, GBK , die viele Zeichen , deren Codierung enthält die gleiche Codierung wie die haben |
, `
, \
(zu nennen das Schlimmste) (auch , dass lächerliche SJIS, auch bekannt als Microsoft Kanji, mit Ausnahme dass es ¥
statt \
, aber immer noch als behandelt wird\
von den meisten Tools wird, da es dort als 0x5c codiert ist).
Wenn Sie sich beispielsweise in einem zh_CN.gb18030
chinesischen Gebietsschema befinden, schreiben Sie ein Skript wie:
echo 詜 reboot
Dieses Skript wird 詜 reboot
in einem Gebietsschema mit GB18030 oder GBK, 唰 reboot
in einem Gebietsschema mit BIG5 oder BIG5-HKSCS, aber in einem C-Gebietsschema mit ASCII oder einem Gebietsschema mit ISO8859-15 oder UTF-8 ausgegeben, reboot
da die GB18030-Codierung ausgeführt wird von 詜
ist 0xd4 0x7c und 0x7c ist die Codierung von |
in ASCII, so dass wir am Ende laufen:
echo �| reboot
(das , das jedoch das 0xd4-Byte darstellt, wird im Gebietsschema gerendert). Beispiel mit dem weniger schädlichen uname
anstelle von reboot
:
$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
\324| uname$
$ LC_ALL=C bash ./myscript | sed -n l
Linux$
( uname
wurde ausgeführt).
Mein Rat wäre also, alle Zeichenfolgen zu zitieren, die Zeichen außerhalb des tragbaren Zeichensatzes enthalten.
Beachten Sie jedoch, dass es besser ist, nicht oder oder (innerhalb dessen und / oder immer noch speziell) zu verwenden, sondern stattdessen Zeichen außerhalb des tragbaren Zeichensatzes zu zitieren , da die Codierung von \
und `
in der Codierung einiger dieser Zeichen enthalten ist.\
"..."
$'...'
`
\
'...'
Ich bin mir nicht bewusst jedes System , das eine locale hat , wo die charset jedes Zeichen hat (außer '
sich selbst natürlich) , dessen Codierung enthält die Kodierung '
, so dass diejenigen , auf '...'
jeden Fall die sicherste sein sollte.
Beachten Sie, dass mehrere Shells auch eine $'\uXXXX'
Notation unterstützen, um Zeichen basierend auf ihrem Unicode-Codepunkt auszudrücken. In Shells wie zsh
und bash
wird das Zeichen codiert in den Zeichensatz des Gebietsschemas eingefügt (kann jedoch zu unerwartetem Verhalten führen, wenn dieser Zeichensatz dieses Zeichen nicht enthält). Auf diese Weise können Sie vermeiden, Nicht-ASCII-Zeichen in Ihren Shell-Code einzufügen.
Also oben:
echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'
Oder:
echo $'voil\u00e0'
echo $'\u8a5c reboot'
(Mit der Einschränkung könnte das Skript beschädigt werden, wenn es in Gebietsschemas ausgeführt wird, in denen diese Zeichen nicht vorhanden sind.)
Oder besser, da dies \
auch etwas Besonderes ist echo
(oder zumindest einige echo
Implementierungen, zumindest die Unix-kompatiblen):
printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '詜 reboot'
(Beachten Sie, dass dies \
auch im ersten Argument für etwas Besonderes ist printf
, sodass Nicht-ASCII-Zeichen dort auch besser vermieden werden, falls sie die Codierung von enthalten können \
.)
Beachten Sie, dass Sie auch Folgendes tun können:
'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'
(Das wäre übertrieben, könnte Ihnen aber Sicherheit geben, wenn Sie nicht sicher sind, welche Zeichen im tragbaren Zeichensatz enthalten sind.)
Stellen Sie außerdem sicher, dass Sie niemals die alte `...`
Form der Befehlssubstitution verwenden (die eine andere Ebene der Backslash-Verarbeitung einführt), sondern $(...)
stattdessen verwenden.
¹ wird technisch echo
auch als Argument an das echo
Dienstprogramm übergeben (um zu sagen, wie es aufgerufen wurde), es ist das argv[0]
und argc
ist 3, obwohl in den meisten Shells heutzutage echo
eingebaut ist, so dass das exec()
einer /bin/echo
Datei mit einer Liste von 3 Argumenten durch das simuliert wird Schale. Es ist auch üblich, die Liste der Argumente so zu betrachten, dass sie mit dem zweiten ( argv[1]
bis argv[argc - 1]
) beginnt , da dies die sind, auf die die Befehle hauptsächlich einwirken.
² eine bemerkenswerte Ausnahme davon ist das lächerliche ja_JP.SJIS
Gebietsschema von FreeBSD-Systemen, deren Zeichensatz weder \
noch ~
Charakter hat!
³ Beachten Sie, dass viele Systeme (FreeBSD, Solaris, jedoch keine GNU-Systeme) U + 00A0 als [[:blank:]]
in UTF-8-Gebietsschemas betrachten, aber nur wenige in anderen Gebietsschemas wie denen, die ISO8859-15 verwenden, möglicherweise, um diese Art von Problem zu vermeiden.