Welche Zeichen müssen in Befehlszeilenargumenten maskiert werden?


14

Welche Zeichen müssen in Bash beim Angeben von Befehlszeilenargumenten für einen Befehl maskiert werden?

Sind sie auf die Metazeichen von Bash beschränkt: Leerzeichen, Tabulator |, &, ;, (, ), <, und >?


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:

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 werden set +H, was die Standardeinstellung bei nicht interaktiven Shells ist.
  • {kann mit deaktiviert werden set +B.
  • *und ?kann mit set -foderset -o noglob deaktiviert 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:

$a =~ s/[\002-\011\013-\032\\\#\?\`\(\)\{\}\[\]\^\*\<\=\>\~\|\; \"\!\$\&\'\202-\377]/\\$&/go;
# quote newline as '\n'                                                                                                         
$a =~ s/[\n]/'\n'/go;

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:

printf "&*\t*!" | parallel --shellquote

Wie habe ich noch nie von Parallelen gehört ...
Tom H

@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 $_;
    }
}

Beispiellauf 1:

$ echo -n "abc" | perl escape_bash_special_chars.pl
abc

Beispiellauf 2:

echo "abc" | perl escape_bash_special_chars.pl
'abc
'

Beispiellauf 3:

echo -n 'ab^c' | perl escape_bash_special_chars.pl
ab^c

Beispiellauf 4:

echo -n 'ab~c' | perl escape_bash_special_chars.pl
'ab~c'

Beispiellauf 5:

echo -n "ab'c" | perl escape_bash_special_chars.pl
'ab'"'"'c'

echo 'ab'"'"'c'
ab'c

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.
Jari Turkia
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.