Wenn Sie unix.stackexchange.com schon länger folgen, sollten Sie jetzt hoffentlich wissen, dass das Nichtanführen einer Variablen im Listenkontext (wie in echo $var
) in Bourne / POSIX-Shells (zsh ist die Ausnahme) eine ganz besondere Bedeutung hat und sollte nicht gemacht werden, es sei denn, Sie haben einen sehr guten Grund dazu.
Es wird ausführlich in einer Reihe von Fragen und Antworten hier (Beispiele erläutert: ? Warum kommt der Shell - Skript Drossel auf Leerzeichen oder andere Sonderzeichen , wenn doppelt zitiert notwendig? , Erweiterung einer Shell - Variablen und Wirkung von glob und Split auf es , Zitat vs unquoted string expansion)
Dies war der Fall seit der ersten Veröffentlichung der Bourne-Shell in den späten 70ern und wurde von der Korn-Shell (eines der größten Bedauern von David Korn (Frage 7) ) nicht geändert oder bash
hat die Korn-Shell größtenteils kopiert, und das ist wie das von POSIX / Unix festgelegt wurde.
Jetzt sehen wir hier immer noch eine Reihe von Antworten und sogar gelegentlich öffentlich veröffentlichten Shell-Code, in dem Variablen nicht in Anführungszeichen gesetzt werden. Sie hätten gedacht, die Leute hätten inzwischen gelernt.
Nach meiner Erfahrung gibt es hauptsächlich drei Arten von Personen, die es unterlassen, ihre Variablen anzugeben:
Anfänger. Diese können entschuldigt werden, da es sich zugegebenermaßen um eine völlig unintuitive Syntax handelt. Und es ist unsere Aufgabe auf dieser Seite, sie auszubilden.
vergessliche Leute.
Leute, die auch nach mehrmaligem Hämmern nicht überzeugt sind, die denken, dass der Bourne-Shell-Autor sicherlich nicht beabsichtigt hat, alle unsere Variablen zu zitieren .
Vielleicht können wir sie überzeugen, wenn wir das mit solchen Verhaltensweisen verbundene Risiko aufdecken.
Was ist das Schlimmste, was passieren kann, wenn Sie vergessen, Ihre Variablen anzugeben? Ist es wirklich so schlimm?
Von welcher Art von Verwundbarkeit sprechen wir hier?
In welchen Zusammenhängen kann es ein Problem sein?
Antworten:
Präambel
Zunächst würde ich sagen, dass dies nicht der richtige Weg ist, um das Problem anzugehen. Es ist ein bisschen so, als würde man sagen " Du sollst keine Menschen ermorden, weil du sonst ins Gefängnis kommst ".
Ebenso zitieren Sie Ihre Variable nicht, weil Sie sonst Sicherheitslücken einführen. Sie zitieren Ihre Variablen, weil es falsch ist, dies nicht zu tun (aber wenn die Angst vor dem Gefängnis helfen kann, warum nicht).
Eine kleine Zusammenfassung für diejenigen, die gerade in den Zug gesprungen sind.
In den meisten Shells hat das Nichtanführen einer variablen Erweiterung (obwohl dies (und der Rest dieser Antwort) auch für die Befehlssubstitution (
`...`
oder$(...)
) und die arithmetische Erweiterung ($((...))
oder$[...]
) gilt) eine ganz besondere Bedeutung. Am besten lässt sich das so beschreiben, als würde man eine Art impliziten split + glob- Operator1 aufrufen .in einer anderen Sprache würde etwa geschrieben werden:
$var
wird zuerst in eine Liste von Wörtern nach komplexen Regeln aufgeteilt, die den$IFS
speziellen Parameter (den geteilten Teil) betreffen, und dann wird jedes Wort, das aus dieser Aufteilung resultiert, als ein Muster betrachtet, das zu einer Liste von Dateien erweitert wird, die mit ihm übereinstimmen (der globale Teil). .Wenn beispielsweise
$var
enthält*.txt,/var/*.xml
und$IFS
enthält,
, wirdcmd
dies mit einer Reihe von Argumenten aufgerufen, wobei das erstecmd
und das nächste dietxt
Dateien im aktuellen Verzeichnis und diexml
Dateien in sind/var
.Wenn Sie nur
cmd
mit den beiden wörtlichen Argumentencmd
und aufrufen möchten*.txt,/var/*.xml
, würden Sie schreiben:was in deiner anderen, bekannteren Sprache wäre:
Was verstehen wir unter Schwachstelle in einer Shell ?
Schließlich ist seit jeher bekannt, dass Shell-Skripte nicht in sicherheitsrelevanten Kontexten verwendet werden sollten. Sicher, OK, eine Variable ohne Anführungszeichen zu lassen, ist ein Fehler, aber das kann nicht so viel Schaden anrichten, oder?
Nun, trotz der Tatsache, dass irgendjemand Ihnen sagen würde, dass Shellskripte niemals für Web - CGIs verwendet werden sollten, oder dass die meisten Systeme heutzutage zum Glück keine setuid / setgid - Shellskripte zulassen, eine Sache, die Shellshock (der aus der Ferne ausnutzbare Bash - Bug, der den Fehler verursachte) Schlagzeilen im September 2014) haben ergeben, dass Shells immer noch häufig dort verwendet werden, wo sie wahrscheinlich nicht verwendet werden sollten: in CGIs, in DHCP-Client-Hook-Skripten, in sudoers-Befehlen, die von (wenn nicht als ) setuid-Befehlen aufgerufen werden ...
Manchmal unwissentlich. Beispielsweise ruft
system('cmd $PATH_INFO')
einphp
/perl
/python
CGI-Skript eine Shell auf, um diese Befehlszeile zu interpretieren (ganz zu schweigen von der Tatsache, dass escmd
sich möglicherweise um ein Shell-Skript handelt und der Autor möglicherweise nie damit gerechnet hat, dass es von einem CGI aufgerufen wird).Sie haben eine Sicherheitslücke, wenn es einen Pfad für die Eskalation von Berechtigungen gibt, dh wenn jemand (nennen wir ihn den Angreifer ) in der Lage ist, etwas zu tun, für das er nicht vorgesehen ist.
Dies bedeutet immer, dass der Angreifer Daten bereitstellt, die von einem privilegierten Benutzer / Prozess verarbeitet werden, der versehentlich etwas tut, was er nicht tun sollte, in den meisten Fällen aufgrund eines Fehlers.
Grundsätzlich haben Sie ein Problem, wenn Ihr Buggy-Code Daten verarbeitet, die vom Angreifer kontrolliert werden .
Nun ist es nicht immer klar, woher diese Daten stammen, und es ist oft schwer zu sagen, ob Ihr Code jemals nicht vertrauenswürdige Daten verarbeiten wird.
In Bezug auf Variablen ist es ziemlich offensichtlich, dass es sich bei den Daten bei einem CGI-Skript um die CGI-GET / POST-Parameter handelt und um Dinge wie Cookies, Pfad, Host ... -Parameter.
Bei einem setuid-Skript (das bei Aufruf durch einen anderen Benutzer als ein Benutzer ausgeführt wird) handelt es sich um die Argumente oder Umgebungsvariablen.
Ein weiterer sehr häufiger Vektor sind Dateinamen. Wenn Sie eine Dateiliste aus einem Verzeichnis erhalten, wurden möglicherweise Dateien vom Angreifer dort abgelegt .
In dieser Hinsicht können Sie selbst auf Aufforderung einer interaktiven Shell verwundbar sein ( z. B. beim Verarbeiten von Dateien in
/tmp
oder~/tmp
).Sogar ein
~/.bashrc
kann verwundbar sein (zum Beispielbash
wird es interpretiert, wenn es aufgerufen wirdssh
, umForcedCommand
ingit
Serverbereitstellungen mit einigen Variablen unter der Kontrolle des Clients ein ähnliches Verhalten auszuführen ).Nun kann ein Skript möglicherweise nicht direkt aufgerufen werden, um nicht vertrauenswürdige Daten zu verarbeiten, aber es kann von einem anderen Befehl aufgerufen werden, der dies tut. Oder Ihr falscher Code wird möglicherweise in Skripten eingefügt, die dies tun (von Ihnen oder einem Ihrer Kollegen nach drei Jahren). Ein besonders kritischer Punkt ist die Beantwortung von Fragen und Antworten, da Sie nie wissen, wo möglicherweise Kopien Ihres Codes landen.
Runter zur Sache; wie schlimm ist es?
Eine Variable (oder eine Befehlsersetzung) nicht in Anführungszeichen zu setzen, ist bei weitem die häufigste Ursache für Sicherheitslücken im Zusammenhang mit Shell-Code. Zum Teil, weil diese Fehler häufig zu Sicherheitslücken führen, aber auch, weil es so häufig vorkommt, dass Variablen nicht in Anführungszeichen gesetzt werden.
Wenn Sie nach Schwachstellen im Shell-Code suchen, müssen Sie zunächst nach Variablen suchen, die nicht in Anführungszeichen stehen. Es ist leicht zu erkennen, oftmals ein guter Kandidat, und es ist im Allgemeinen einfach, auf angreifergesteuerte Daten zurückzugreifen.
Es gibt unendlich viele Möglichkeiten, wie eine Variable ohne Anführungszeichen zu einer Sicherheitsanfälligkeit werden kann. Ich werde hier nur einige allgemeine Trends nennen.
Offenlegung von Informationen
Die meisten Leute werden aufgrund des geteilten Teils auf Fehler stoßen, die mit nicht zitierten Variablen zusammenhängen (zum Beispiel ist es heutzutage üblich, dass Dateien Leerzeichen in ihren Namen haben und Leerzeichen im Standardwert von IFS sind). Viele Leute werden den Glob- Teil übersehen . Der Glob- Teil ist mindestens so gefährlich wie der Split- Teil.
Wenn der Angreifer nach einer nicht bereinigten externen Eingabe eine Globulation durchführt, kann er Sie dazu bringen, den Inhalt eines beliebigen Verzeichnisses zu lesen.
Im:
Wenn
$unsanitised_external_input
enthält/*
, bedeutet dies, dass der Angreifer den Inhalt von sehen kann/
. Keine große Sache. Es wird interessanter , wenn auch mit/home/*
denen gibt Ihnen eine Liste von Benutzernamen auf der Maschine,/tmp/*
,/home/*/.forward
für Hinweise auf andere gefährliche Praktiken,/etc/rc*/*
für aktivierte Dienste ... Keine Notwendigkeit , sie einzeln zu nennen. Ein Wert von/* /*/* /*/*/*...
listet nur das gesamte Dateisystem auf.Denial-of-Service-Schwachstellen.
Nehmen wir den vorherigen Fall etwas zu weit und wir haben ein DoS.
Tatsächlich ist jede nicht in Anführungszeichen gesetzte Variable im Listenkontext mit nicht bereinigter Eingabe mindestens eine DoS-Sicherheitsanfälligkeit.
Sogar erfahrene Shell-Skripter vergessen häufig, Dinge zu zitieren wie:
:
ist der no-op Befehl. Was könnte möglicherweise falsch laufen?Das soll zuweisen
$1
,$QUERYSTRING
wenn nicht gesetzt$QUERYSTRING
war. Dies ist eine schnelle Möglichkeit, ein CGI-Skript auch über die Befehlszeile aufrufbar zu machen.Das
$QUERYSTRING
ist jedoch immer noch erweitert und da es nicht in Anführungszeichen steht, wird der split + glob- Operator aufgerufen.Nun gibt es einige Globs, deren Erweiterung besonders teuer ist. Das
/*/*/*/*
ist schon schlimm genug, da es bedeutet, dass Verzeichnisse bis zu 4 Ebenen tiefer aufgelistet werden. Zusätzlich zur Festplatten- und CPU-Aktivität bedeutet dies das Speichern von Zehntausenden von Dateipfaden (40 KB hier auf einer minimalen Server-VM, 10 KB davon Verzeichnisse).Jetzt
/*/*/*/*/../../../../*/*/*/*
heißt es 40k x 10k und/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*
reicht aus, um selbst die mächtigste Maschine in die Knie zu zwingen.Probieren Sie es selbst aus (seien Sie jedoch darauf vorbereitet, dass Ihre Maschine abstürzt oder hängen bleibt):
Natürlich, wenn der Code ist:
Dann können Sie die Festplatte füllen.
Führen Sie einfach eine Google-Suche in Shell-CGI oder Bash-CGI oder Ksh-CGI durch , und Sie werden einige Seiten finden, die Ihnen zeigen, wie Sie CGIs in Shells schreiben. Beachten Sie, wie die Hälfte dieser Prozessparameter anfällig ist.
Sogar der von David Korn ist verwundbar (siehe Cookie-Behandlung).
bis zu beliebigen Sicherheitslücken bei der Codeausführung
Die Ausführung von willkürlichem Code ist die schwerwiegendste Sicherheitsanfälligkeit, da der Angreifer unbegrenzte Möglichkeiten hat, Befehle auszuführen.
Das ist im Allgemeinen der geteilte Teil, der zu diesen führt. Diese Aufteilung führt dazu, dass mehrere Argumente an Befehle übergeben werden, wenn nur eines erwartet wird. Während die erste davon im erwarteten Kontext verwendet wird, werden die anderen in einem anderen Kontext verwendet, sodass sie möglicherweise unterschiedlich interpretiert werden. Besser mit einem Beispiel:
Hier sollte der Inhalt der
$external_input
Shell-Variablen derfoo
awk
Variablen zugewiesen werden.Jetzt:
Das zweite Wort, das sich aus der Aufteilung von
$external_input
ergibt, wird nicht zugewiesen,foo
sondern alsawk
Code betrachtet (hier wird ein beliebiger Befehl ausgeführt:)uname
.Das ist vor allem ein Problem für Befehle , die anderen Befehle ausführen kann (
awk
,env
,sed
(GNU eins)perl
,find
...) vor allem mit den GNU - Varianten (die Optionen , die nach Argumenten akzeptieren). Manchmal würde man keine Befehle vermuten, um andere ausführen zu können wieksh
,bash
oderzsh
's[
oderprintf
...Wenn wir ein Verzeichnis namens erstellen
x -o yes
, wird der Test positiv, da es sich um einen völlig anderen bedingten Ausdruck handelt, den wir auswerten.Schlimmer noch, wenn wir eine Datei namens erstellen
x -a a[0$(uname>&2)] -gt 1
, die mindestens mit allen ksh-Implementierungen (einschließlich dersh
meisten kommerziellen Unices und einiger BSDs) ausgeführt wird,uname
weil diese Shells arithmetische Auswertungen für die numerischen Vergleichsoperatoren des[
Befehls durchführen.Gleiches gilt
bash
für einen Dateinamen wiex -a -v a[0$(uname>&2)]
.Wenn sie nicht willkürlich hingerichtet werden können, kann sich der Angreifer natürlich mit weniger Schaden zufrieden geben (was helfen kann, willkürlich hingerichtet zu werden). Jeder Befehl, der Dateien schreiben oder Berechtigungen, Besitz oder Nebeneffekte ändern kann, kann ausgenutzt werden.
Mit Dateinamen können alle möglichen Dinge erledigt werden.
Und am Ende machst du es beschreibbar
..
(rekursiv mit GNUchmod
).Skripte, die die automatische Verarbeitung von Dateien in öffentlich beschreibbaren Bereichen durchführen
/tmp
, müssen sehr sorgfältig geschrieben werden.Wie wäre es mit
[ $# -gt 1 ]
Das finde ich ärgerlich. Einige Leute haben die Mühe, sich zu fragen, ob eine bestimmte Erweiterung problematisch sein könnte, um zu entscheiden, ob sie die Anführungszeichen weglassen können.
Es ist wie zu sagen. Hey, es sieht so aus,
$#
als könnte der split + glob-Operator nicht verwendet werden. Bitten wir die Shell, ihn zu split + globen . Oder Hey, lass uns falschen Code schreiben, nur weil es unwahrscheinlich ist, dass der Fehler auftritt .Wie unwahrscheinlich ist es jetzt? OK,
$#
(oder$!
,$?
oder eine arithmetische Substitution) nur Ziffern enthalten (oder-
für einige) , so dass die glob Teil ist , aus. Damit der aufgeteilte Teil jedoch etwas bewirkt, müssen wir$IFS
nur Ziffern (oder-
) enthalten.Mit einigen Shells
$IFS
kann von der Umgebung geerbt werden, aber wenn die Umgebung nicht sicher ist, ist das Spiel trotzdem vorbei.Wenn Sie nun eine Funktion schreiben wie:
Das bedeutet, dass das Verhalten Ihrer Funktion vom Kontext abhängt, in dem sie aufgerufen wird. Oder mit anderen Worten,
$IFS
wird einer der Eingänge dazu. Genau genommen sollte es beim Schreiben der API-Dokumentation für Ihre Funktion ungefähr so aussehen:Und Code, der Ihre Funktion aufruft, muss sicherstellen, dass er
$IFS
keine Ziffern enthält. Das alles, weil Sie keine Lust hatten, diese 2 doppelten Anführungszeichen einzugeben.Damit dieser
[ $# -eq 2 ]
Bug zu einer Sicherheitslücke wird, müsste der Wert von irgendwie$IFS
unter die Kontrolle des Angreifers geraten . Möglicherweise würde dies normalerweise nicht passieren, wenn der Angreifer nicht einen anderen Fehler ausnutzen könnte.Das ist allerdings nicht ungewöhnlich. Ein häufiger Fall ist, dass Benutzer vergessen, Daten zu bereinigen, bevor sie in arithmetischen Ausdrücken verwendet werden. Wir haben oben bereits gesehen, dass es in einigen Shells die Ausführung von willkürlichem Code ermöglichen kann, aber in allen erlaubt es dem Angreifer , jeder Variablen einen ganzzahligen Wert zuzuweisen.
Zum Beispiel:
Und mit einem
$1
mit Wert(IFS=-1234567890)
hat diese arithmetische Auswertung den Nebeneffekt, dass IFS gesetzt wird und der nächste[
Befehl fehlschlägt, was bedeutet, dass die Prüfung auf zu viele Argumente umgangen wird.Was ist, wenn der split + glob- Operator nicht aufgerufen wird?
Es gibt einen anderen Fall, in dem Anführungszeichen für Variablen und andere Erweiterungen erforderlich sind: wenn sie als Muster verwendet werden.
Testen Sie nicht, ob
$a
und gleich$b
sind (außer mitzsh
), sondern ob$a
das Muster mit übereinstimmt$b
. Und Sie müssen zitieren,$b
wenn Sie als Zeichenfolgen vergleichen möchten (dasselbe in"${a#$b}"
oder"${a%$b}"
oder"${a##*$b*}"
wo$b
sollte zitiert werden, wenn es nicht als Muster verwendet werden soll).Dies bedeutet, dass
[[ $a = $b ]]
in Fällen, in denen dies nicht der Fall$a
ist$b
(z. B. wann$a
istanything
und$b
ist*
), true zurückgegeben werden kann oder false zurückgegeben werden kann, wenn sie identisch sind (z. B. wenn beide$a
und$b
sind[a]
).Kann dies zu einer Sicherheitslücke führen? Ja, wie jeder Käfer. Hier kann der Angreifer den logischen Codefluss Ihres Skripts ändern und / oder die Annahmen Ihres Skripts brechen. Zum Beispiel mit einem Code wie:
Der Angreifer kann den Scheck durch Überholen umgehen
'[a]' '[a]'
.Wenn weder dieser Mustervergleich noch der Operator split + glob zutreffen, besteht dann die Gefahr, dass eine Variable nicht in Anführungszeichen gesetzt wird.
Ich muss zugeben, dass ich schreibe:
Dort schadet das Zitieren nicht, ist aber nicht zwingend erforderlich.
Ein Nebeneffekt des Weglassens von Anführungszeichen in diesen Fällen (z. B. bei Fragen und Antworten) ist jedoch, dass Anfängern eine falsche Nachricht gesendet werden kann:
Möglicherweise ist es in Ordnung, keine Variablen anzugeben.Zum Beispiel könnten sie anfangen zu denken, dass, wenn
a=$b
OK ist,dies auch der Fall ist (was in vielen Shells nicht so ist wie in Argumenten für denexport a=$b
export
Befehl, also im Listenkontext) oder.env a=$b
Was ist
zsh
?zsh
Die meisten dieser Designprobleme wurden behoben. Inzsh
(zumindest , wenn sie nicht in sh / KSH - Emulationsmodus), wenn Sie wollen Spaltung oder Globbing oder Pattern - Matching , müssen Sie es explizit fordern:$=var
aufzuspalten, und$~var
für den Inhalt der Variablen zu glob oder wie behandelt werden ein Muster.Das Aufteilen (aber nicht das Verschieben) erfolgt jedoch implizit nach einer nicht zitierten Befehlssubstitution (wie in
echo $(cmd)
).Ein manchmal unerwünschter Nebeneffekt, wenn keine Variable angegeben wird, ist die Leergutentfernung . Das
zsh
Verhalten ähnelt dem, was Sie in anderen Shells erreichen können, indem Sie das Globbing insgesamt (mitset -f
) und das Teilen (mitIFS=''
) deaktivieren . Immer noch in:Es wird kein split + glob geben , aber wenn
$var
leer, anstatt ein leeres Argument zu erhalten,cmd
wird überhaupt kein Argument erhalten.Das kann Fehler verursachen (wie das Offensichtliche
[ -n $var ]
). Das kann möglicherweise die Erwartungen und Annahmen eines Skripts brechen und Sicherheitslücken verursachen, aber ich kann mir gerade kein nicht allzu weit hergeholtes Beispiel einfallen lassen.Was ist, wenn Sie tun das brauchen Split + glob Operator?
Ja, in der Regel möchten Sie Ihre Variable nicht in Anführungszeichen setzen. Aber dann müssen Sie sicherstellen, dass Sie Ihre Split- und Glob- Operatoren richtig einstellen , bevor Sie sie verwenden. Wenn Sie nur den geteilten Teil und nicht den Glob- Teil (was meistens der Fall ist) möchten , müssen Sie das Globbing (
set -o noglob
/set -f
) deaktivieren und korrigieren$IFS
. Andernfalls verursachen Sie ebenfalls Sicherheitslücken (wie das oben erwähnte CGI-Beispiel von David Korn).Fazit
Kurz gesagt, es kann sehr gefährlich sein, eine Variable (oder eine Befehlssubstitution oder eine arithmetische Erweiterung) ohne Anführungszeichen in Shells zu lassen, insbesondere wenn sie in falschen Kontexten ausgeführt wird.
Das ist einer der Gründe, warum es als schlechte Praxis gilt .
Vielen Dank fürs Lesen. Wenn es dir über den Kopf geht, mach dir keine Sorgen. Man kann nicht erwarten, dass jeder versteht, was es bedeutet, seinen Code so zu schreiben, wie er geschrieben wurde. Aus diesem Grund haben wir Empfehlungen für bewährte Praktiken , sodass diese befolgt werden können, ohne unbedingt zu verstehen, warum.
(Falls dies noch nicht offensichtlich ist, vermeiden Sie es, sicherheitsrelevanten Code in Shells zu schreiben.)
Und bitte geben Sie Ihre Variablen in Ihren Antworten auf dieser Website an!
¹In
ksh93
undpdksh
und Ableitungen wird die Erweiterung von geschweiften Klammern auch durchgeführt, sofern das Globbing nicht deaktiviert ist (beiksh93
Versionen bis zu ksh93u +, auch wenn diebraceexpand
Option deaktiviert ist).quelle
[[
nur die RHS der Vergleiche angegeben werden muss:if [[ $1 = "$2" ]]; then
[[ $* = "$var" ]]
nicht dasselbe ist, als[[ "$*" = "$var" ]]
wenn das erste Zeichen von$IFS
kein Leerzeichen istbash
(und auchmksh
wenn$IFS
leer ist, obwohl ich in diesem Fall nicht sicher bin, was$*
gleich ist, sollte ich einen Fehler auslösen?)).foo='bar; rm *'
nicht der Fall sein. Es wird jedoch der Inhalt des aktuellen Verzeichnisses aufgeführt, der als Offenlegung von Informationen gelten kann.print $foo
inksh93
(woprint
ist der Ersatz fürecho
diese Adressen einige seiner Mängel) hat zwar eine Code-Injection-Schwachstelle (zum Beispiel mitfoo='-f%.0d z[0$(uname>&2)]'
) (Sie tatsächlich brauchenprint -r -- "$foo"
.echo "$foo"
ist immer noch falsch und nicht behebbar (obwohl im Allgemeinen weniger schädlich)).[Inspiriert von dieser Antwort von cas .]
Aber was wenn …?
Kann ich nicht sagen ; Das schlägt fehl, wenn die Zeichenfolge leer ist.
grep "$ignorecase" other_grep_args
$ignorecase
Antwort:
Wie in der anderen Antwort besprochen, schlägt dies immer noch fehl, wenn
IFS
ein-
oder ein enthalten isti
. Wenn Sie sichergestellt habenIFS
, dass Ihre Variable kein Zeichen enthält (und Sie sicher sind, dass Ihre Variable keine Glob-Zeichen enthält), ist dies wahrscheinlich sicher.Aber es gibt einen sichereren Weg (obwohl er etwas hässlich und nicht intuitiv ist): Verwenden
${ignorecase:+"$ignorecase"}
. Von der POSIX - Shell Command Language - Spezifikation unter 2.6.2 Parameter Expansion ,Der Trick dabei ist, dass wir
ignorecase
alsparameter
und"$ignorecase"
als verwendenword
. Also${ignorecase:+"$ignorecase"}
heißtDas bringt uns dahin, wohin wir wollen: Wenn die Variable auf die leere Zeichenfolge gesetzt ist, wird sie "entfernt" (dieser gesamte, verschachtelte Ausdruck wird zu nichts ausgewertet - nicht einmal zu einer leeren Zeichenfolge), und wenn die Variable eine Nicht-Zeichenfolge enthält -leerer Wert, wir bekommen diesen Wert in Anführungszeichen.
Aber was wenn …?
Antwort:
Sie könnten denken, dass dies ein Fall für die Verwendung istNein! Widerstehen Sie der Versuchung, auch nur über die Verwendungeval
.eval
hier nachzudenken .Auch wenn Sie sichergestellt haben, dass
IFS
Ihre Variable kein Zeichen enthält (mit Ausnahme der Leerzeichen, die Sie berücksichtigen möchten) und Sie sicher sind, dass Ihre Variable keine Glob-Zeichen enthält, ist dies wahrscheinlich der Fall sicher.Wenn Sie jedoch bash (oder ksh, zsh oder yash) verwenden, ist Folgendes sicherer: Verwenden Sie ein Array:
Von bash (1) ,
So
"${criteria[@]}"
dehnt sich (im obigen Beispiel) , die null, zwei oder vier Elemente descriteria
Arrays, die jeweils angegeben. Insbesondere, wenn keine der Bedingungen s wahr ist, hat dascriteria
Array keinen Inhalt (wie in dercriteria=()
Anweisung festgelegt) und wird"${criteria[@]}"
zu nichts ausgewertet (nicht einmal zu einer unbequemen leeren Zeichenfolge).Dies wird besonders interessant und kompliziert, wenn Sie mit mehreren Wörtern arbeiten, von denen einige dynamische (Benutzer-) Eingaben sind, die Sie nicht im Voraus kennen und die Leerzeichen oder andere Sonderzeichen enthalten können. Erwägen:
Beachten Sie, dass dies
$fname
bei jeder Verwendung in Anführungszeichen steht. Dies funktioniert auch, wenn der Benutzer etwas wiefoo bar
oder eingibtfoo*
."${criteria[@]}"
wertet zu-name "foo bar"
oder aus-name "foo*"
. (Denken Sie daran, dass jedes Element des Arrays in Anführungszeichen steht.)Arrays funktionieren nicht in allen POSIX-Shells. Arrays sind ksh / bash / zsh / yash-ism. Außer… es gibt ein Array, das alle Shells unterstützen: die Argumentliste, auch bekannt als
"$@"
. Wenn Sie mit der Argumentliste fertig sind, mit der Sie aufgerufen wurden (z. B. haben Sie alle „Positionsparameter“ (Argumente) in Variablen kopiert oder auf andere Weise verarbeitet), können Sie die Argumentliste als Array verwenden:Das
"$@"
Konstrukt (das historisch gesehen an erster Stelle stand) hat die gleiche Semantik wie - es erweitert jedes Argument (dh jedes Element der Argumentliste) zu einem separaten Wort, als hätten Sie es eingegeben ."${name[@]}"
"$1" "$2" "$3" …
Auszug aus der Spezifikation der POSIX Shell Command Language unter 2.5.2 Special Parameters ,
Der vollständige Text ist etwas kryptisch; Der entscheidende Punkt ist, dass angegeben wird, dass
"$@"
Nullfelder generiert werden sollen, wenn keine Positionsparameter vorhanden sind. Historischer Hinweis: Als es"$@"
1979 zum ersten Mal in der Bourne-Shell (dem Vorgänger von Bash) eingeführt wurde, gab es einen Fehler,"$@"
der durch eine einzelne leere Zeichenfolge ersetzt wurde, wenn keine Positionsparameter vorhanden waren. Siehe Was${1+"$@"}
bedeutet in einem Shell-Skript und wie unterscheidet es sich von"$@"
? , The Traditional Bourne Shell Family , Was${1+"$@"}
bedeutet ... und wo ist es notwendig? und"$@"
versus${1+"$@"}
.Arrays helfen auch in der ersten Situation:
____________________
PS (csh)
Dies sollte selbstverständlich sein, aber für die Leute, die hier neu sind: csh, tcsh usw., sind keine Bourne / POSIX-Shells. Sie sind eine ganz andere Familie. Ein Pferd einer anderen Farbe. Ein ganz anderes Ballspiel. Eine andere Katzenrasse. Vögel einer anderen Feder. Und vor allem eine andere Dose Würmer.
Einige der Aussagen auf dieser Seite beziehen sich auf csh. Zum Beispiel: Es ist eine gute Idee, alle Variablen in Anführungszeichen zu setzen, es sei denn, Sie haben einen guten Grund, dies nicht zu tun, und Sie sind sicher, dass Sie wissen, was Sie tun. Aber in csh ist jede Variable ein Array - es kommt einfach so vor, dass fast jede Variable ein Array von nur einem Element ist und sich ziemlich ähnlich wie eine gewöhnliche Shell-Variable in Bourne / POSIX-Shells verhält. Und die Syntax ist schrecklich anders (und ich meine schrecklich ). Deshalb werden wir hier nichts mehr über die csh-Familie sagen.
quelle
csh
Sie in lieber$var:q
als verwenden,"$var"
da letzteres nicht für Variablen funktioniert, die Zeilenumbruchzeichen enthalten (oder wenn Sie die Elemente des Arrays einzeln in Anführungszeichen setzen möchten, anstatt sie mit Leerzeichen zu einem einzigen Argument zu verbinden).bitrate="--bitrate 320"
mit${bitrate:+$bitrate}
undbitrate=--bitrate 128
wird nicht funktionieren,${bitrate:+"$bitrate"}
da der Befehl abgebrochen wird. Ist es sicher${variable:+$variable}
mit nein zu benutzen"
?$@
für etwas anderes verwenden) oder Sie sich einfach aus anderen Gründen weigern, ein Array zu verwenden, verweise ich darauf Sie zu "Wie in der anderen Antwort besprochen". … (Fortsetzung)${variable:+$variable}
ohne"
) fehl , wenn IFS enthält-
,0
,2
,3
,a
,b
,e
,i
,r
odert
.${bitrate:+$bitrate}
Ich war skeptisch gegenüber Stéphanes Antwort, es ist jedoch möglich, Folgendes zu missbrauchen
$#
:oder $ ?:
Dies sind erfundene Beispiele, aber das Potenzial besteht.
quelle