Vergessen Sie nicht (möglichen) Dateinamen mit * und?
Jeff Schaller
Vielen Dank. Können Sie die Arten von Zeichen, die maskiert werden müssen, in cmd-Zeilenargumenten vollständig auflisten?
StackExchange for All
Die Liste ist gut zu haben, aber das Wichtigste, was man beim Zitieren verstehen muss, ist: Alles zwischen einfachen Anführungszeichen wird wörtlich und ohne Worttrennung übergeben. Keine Ausnahmen. (Dies bedeutet, dass es keine Möglichkeit gibt, ein einzelnes Anführungszeichen in einfache Anführungszeichen einzubetten, aber das lässt sich leicht umgehen .)
Wildcard,
Antworten:
22
Die folgenden Zeichen haben in einigen Kontexten eine besondere Bedeutung für die Shell selbst und müssen möglicherweise in Argumenten maskiert werden:
=Das Gleichheitszeichen (U + 003D) muss ebenfalls maskiert werden, wenn set -koderset -o keyword aktiviert ist.
Ein Zeilenumbruch muss in Anführungszeichen gesetzt werden - umgekehrte Schrägstriche reichen nicht aus. Alle anderen in IFS aufgelisteten Zeichen müssen ähnlich behandelt werden. Sie müssen nicht entkommen müssen ]oder }aber Sie tun müssen , entkommen )ein Operator , weil es.
Einige dieser Charaktere haben strengere Grenzen, wann sie wirklich fliehen müssen als andere. Zum Beispiel a#bist in Ordnung, ist aber a #bein Kommentar, während >in beiden Kontexten ein Escape erforderlich wäre. Es tut sowieso nicht weh, ihnen allen konservativ zu entkommen, und es ist einfacher, sich an die feinen Unterschiede zu erinnern.
Wenn Ihr Befehlsnamen selbst ein Shell - Schlüsselwort ist ( if, for, do) , dann werden Sie brauchen , um es zu entkommen oder zu zitieren. Das einzig interessante ist in, dass es sich nicht immer um ein Schlüsselwort handelt. Sie müssen das nicht für Schlüsselwörter tun, die in Argumenten verwendet werden, sondern nur, wenn Sie (dummerweise!) Einen Befehl nach einem von ihnen benannt haben. Shell-Operatoren ( (, &usw.) müssen immer und überall zitiert werden.
1 Stéphane hat bemerkt, dass jedes andere Einzelbyte- Leerzeichen in Ihrem Gebietsschema ebenfalls maskiert werden muss. In den meisten gängigen, vernünftigen Gebietsschemata, zumindest denen, die auf C oder UTF-8 basieren, sind es nur die oben genannten Leerzeichen. In einigen ISO-8859-1-Ländereinstellungen wird der unterbrechungsfreie Speicherplatz U + 00A0 als leer betrachtet, einschließlich Solaris, BSDs und OS X (ich denke falsch). Wenn es sich um ein beliebiges unbekanntes Gebietsschema handelt, kann es so gut wie alles enthalten, einschließlich Buchstaben, also viel Glück.
Es ist vorstellbar, dass ein einzelnes Byte, das als leer betrachtet wird, in einem Multi-Byte-Zeichen erscheint, das nicht leer ist, und Sie können sich dem nur entziehen, wenn Sie das Ganze in Anführungszeichen setzen. Dies ist kein theoretisches Problem: In einem ISO-8859-1-Gebietsschema von oben kann dieses A0Byte, das als Leerzeichen betrachtet wird, in Multibyte-Zeichen wie UTF-8-codiert "à" ( C3 A0) auftreten. Um mit diesen Zeichen sicher umzugehen, müssten Sie sie in Anführungszeichen setzen "à". Dieses Verhalten hängt von der Länderkonfiguration in der Umgebung ab, in der das Skript ausgeführt wird, und nicht von der Konfiguration, in der Sie es geschrieben haben.
Ich denke, dieses Verhalten ist in mehrfacher Hinsicht problematisch, aber wir müssen die Hand spielen, die wir bekommen. Wenn Sie mit einem nicht selbstsynchronisierenden Multibyte-Zeichensatz arbeiten, ist es am sichersten, alles in Anführungszeichen zu setzen. Wenn Sie sich in UTF-8 oder C befinden, sind Sie (im Moment) in Sicherheit.
Andere Leerzeichen in Ihrem Gebietsschema müssen ebenfalls mit einem Escape-
Zeichen versehen werden
Sie müssen nur entkommen, !wenn die Erweiterung des csh-Verlaufs aktiviert ist, normalerweise nicht in Skripten. [ ! -f a ]oder find . ! -name...sind in Ordnung. Dies wird in Ihrem Abschnitt zu engeren Limits behandelt, ist aber möglicherweise ausdrücklich erwähnenswert.
Stéphane Chazelas
Beachten Sie, dass es Kontexte , in denen andere Zeichen müssen zitiert wie: hash[foo"]"]=, ${var-foo"}"}, [[ "!" = b ]], [[ a = "]]" ]], die regexp Operatoren [[ x =~ ".+[" ]]. Andere Keywords als {( if, while, for...) müssten zitiert werden , so sind sie nicht als solche erkannt ...
Stéphane Chazelas
Soweit es sich überhaupt um Befehlszeilenargumente handelt, hängt die Interpretation von dem fraglichen Befehl ab (genau wie ]), daher liste ich sie nicht auf. Ich glaube nicht, dass ein Schlüsselwort in der Argumentposition zitiert werden muss.
Michael Homer
2
Das Zitieren von Builtins, Bindestrichen oder% hat keine Auswirkung.
Michael Homer
3
In GNU Parallel wird dies ausgiebig getestet und verwendet:
Es wird geprüft , in bash, dash, ash, ksh, zsh, und fish. Einige der Charaktere müssen in einigen (Versionen) der Shells nicht zitiert werden, aber das Obige funktioniert in allen getesteten Shells.
Wenn Sie einfach eine Zeichenfolge in Anführungszeichen setzen möchten, können Sie sie wie folgt weiterleiten parallel --shellquote:
@TomH Es wird geschätzt, wenn Sie 5 Minuten darüber nachdenken können, wie wir Sie hätten erreichen können.
Ole Tange
Ich denke, es ist ein Fortschrittsproblem. Die meisten Menschen brauchen oder verstehen nicht parallel, bis sie einige Komplexitätsstufen durchlaufen haben. Zu welchem Zeitpunkt sind sie auf Xargs, Nohup und ähnliches gestoßen? Außerdem sehe ich nicht viele Leute, die parallel arbeiten, um Probleme beim Stapelaustausch zu lösen, oder wenn ich nach Lösungen für Bash-Probleme suche
Tom H
1
Bei der einfachen Escape-Lösung in Perl verfolge ich das Prinzip der einfachen Anführungszeichen. Ein Bash-String in einfachen Anführungszeichen kann ein beliebiges Zeichen haben, mit Ausnahme des einfachen Anführungszeichens.
Mein Code:
my $bash_reserved_characters_re = qr([!"#$&'()*;<>?\[\\`{|~\t\n]);
while(<>) {
if (/$bash_reserved_characters_re/) {
my $quoted = s/'/'"'"'/gr;
print "'$quoted'";}else{
print $_;}}
Ja, das ist ein gültiger Punkt. Meiner Ansicht nach werden die meisten Leute auf dieser Seite landen, weil sie ein Problem zu lösen haben. Nicht, weil dies eine interessante akademische Debatte darstellt. Aus diesem Grund möchte ich Lösungen anbieten und deren Vorzüge diskutieren, auch wenn ich etwas abseits des Themas stehe.
Jari Turkia
Mein Code ist nur eine Implementierung von Michael Homers Antwort. Ich wollte nicht mehr Informationen bringen als er.
Antworten:
Die folgenden Zeichen haben in einigen Kontexten eine besondere Bedeutung für die Shell selbst und müssen möglicherweise in Argumenten maskiert werden:
`
Backtick (U + 0060 Grabakzent )~
Tilde (U + 007E)!
Ausrufezeichen (U + 0021)#
Hash (U + 0023 Nummernzeichen)$
Dollarzeichen (U + 0024)&
Et-Zeichen (U + 0026)*
Sternchen (U + 002A)(
Linke Klammer (U + 0028))
Rechte Klammer (U + 0029)(
⇥
) Tab (U + 0009){
Linke Klammer (U + 007B Linke geschweifte Klammer)[
Linke eckige Klammer (U + 005B)|
Vertikaler Balken (vertikale Linie U + 007C)\
Backslash (U + 005C umgekehrter Solidus);
Semikolon (U + 003B)'
Einfaches Anführungszeichen / Apostroph (U + 0027)"
Doppelte Anführungszeichen (U + 0022)↩
Neue Linie (U + 000A)<
Weniger als (U + 003C)>
Größer als (U + 003E)?
Fragezeichen (U + 003F)Leertaste (U + 0020) 1
Einige dieser Zeichen werden für mehr Dinge und an mehr Stellen als die von mir verknüpften verwendet.
Es gibt einige Eckfälle, die ausdrücklich optional sind:
!
kann mit deaktiviert werdenset +H
, was die Standardeinstellung bei nicht interaktiven Shells ist.{
kann mit deaktiviert werdenset +B
.*
und?
kann mitset -f
oderset -o noglob
deaktiviert werden .=
Das Gleichheitszeichen (U + 003D) muss ebenfalls maskiert werden, wennset -k
oderset -o keyword
aktiviert ist.Ein Zeilenumbruch muss in Anführungszeichen gesetzt werden - umgekehrte Schrägstriche reichen nicht aus. Alle anderen in IFS aufgelisteten Zeichen müssen ähnlich behandelt werden. Sie müssen nicht entkommen müssen
]
oder}
aber Sie tun müssen , entkommen)
ein Operator , weil es.Einige dieser Charaktere haben strengere Grenzen, wann sie wirklich fliehen müssen als andere. Zum Beispiel
a#b
ist in Ordnung, ist abera #b
ein Kommentar, während>
in beiden Kontexten ein Escape erforderlich wäre. Es tut sowieso nicht weh, ihnen allen konservativ zu entkommen, und es ist einfacher, sich an die feinen Unterschiede zu erinnern.Wenn Ihr Befehlsnamen selbst ein Shell - Schlüsselwort ist (
if
,for
,do
) , dann werden Sie brauchen , um es zu entkommen oder zu zitieren. Das einzig interessante istin
, dass es sich nicht immer um ein Schlüsselwort handelt. Sie müssen das nicht für Schlüsselwörter tun, die in Argumenten verwendet werden, sondern nur, wenn Sie (dummerweise!) Einen Befehl nach einem von ihnen benannt haben. Shell-Operatoren ((
,&
usw.) müssen immer und überall zitiert werden.1 Stéphane hat bemerkt, dass jedes andere Einzelbyte- Leerzeichen in Ihrem Gebietsschema ebenfalls maskiert werden muss. In den meisten gängigen, vernünftigen Gebietsschemata, zumindest denen, die auf C oder UTF-8 basieren, sind es nur die oben genannten Leerzeichen. In einigen ISO-8859-1-Ländereinstellungen wird der unterbrechungsfreie Speicherplatz U + 00A0 als leer betrachtet, einschließlich Solaris, BSDs und OS X (ich denke falsch). Wenn es sich um ein beliebiges unbekanntes Gebietsschema handelt, kann es so gut wie alles enthalten, einschließlich Buchstaben, also viel Glück.
Es ist vorstellbar, dass ein einzelnes Byte, das als leer betrachtet wird, in einem Multi-Byte-Zeichen erscheint, das nicht leer ist, und Sie können sich dem nur entziehen, wenn Sie das Ganze in Anführungszeichen setzen. Dies ist kein theoretisches Problem: In einem ISO-8859-1-Gebietsschema von oben kann dieses
A0
Byte, das als Leerzeichen betrachtet wird, in Multibyte-Zeichen wie UTF-8-codiert "à" (C3 A0
) auftreten. Um mit diesen Zeichen sicher umzugehen, müssten Sie sie in Anführungszeichen setzen"à"
. Dieses Verhalten hängt von der Länderkonfiguration in der Umgebung ab, in der das Skript ausgeführt wird, und nicht von der Konfiguration, in der Sie es geschrieben haben.Ich denke, dieses Verhalten ist in mehrfacher Hinsicht problematisch, aber wir müssen die Hand spielen, die wir bekommen. Wenn Sie mit einem nicht selbstsynchronisierenden Multibyte-Zeichensatz arbeiten, ist es am sichersten, alles in Anführungszeichen zu setzen. Wenn Sie sich in UTF-8 oder C befinden, sind Sie (im Moment) in Sicherheit.
quelle
!
wenn die Erweiterung des csh-Verlaufs aktiviert ist, normalerweise nicht in Skripten.[ ! -f a ]
oderfind . ! -name...
sind in Ordnung. Dies wird in Ihrem Abschnitt zu engeren Limits behandelt, ist aber möglicherweise ausdrücklich erwähnenswert.hash[foo"]"]=
,${var-foo"}"}
,[[ "!" = b ]]
,[[ a = "]]" ]]
, die regexp Operatoren[[ x =~ ".+[" ]]
. Andere Keywords als{
(if
,while
,for
...) müssten zitiert werden , so sind sie nicht als solche erkannt ...]
), daher liste ich sie nicht auf. Ich glaube nicht, dass ein Schlüsselwort in der Argumentposition zitiert werden muss.In GNU Parallel wird dies ausgiebig getestet und verwendet:
Es wird geprüft , in
bash
,dash
,ash
,ksh
,zsh
, undfish
. Einige der Charaktere müssen in einigen (Versionen) der Shells nicht zitiert werden, aber das Obige funktioniert in allen getesteten Shells.Wenn Sie einfach eine Zeichenfolge in Anführungszeichen setzen möchten, können Sie sie wie folgt weiterleiten
parallel --shellquote
:quelle
Bei der einfachen Escape-Lösung in Perl verfolge ich das Prinzip der einfachen Anführungszeichen. Ein Bash-String in einfachen Anführungszeichen kann ein beliebiges Zeichen haben, mit Ausnahme des einfachen Anführungszeichens.
Mein Code:
Beispiellauf 1:
Beispiellauf 2:
Beispiellauf 3:
Beispiellauf 4:
Beispiellauf 5:
quelle