(OK, tut mir leid, ich habe Ihre Frage zu schnell gelesen, daher ist ein Teil meiner Antwort etwas nebensächlich und lässt sie immer noch so, wie sie für Sie oder andere nützlich sein kann.)
Hier sind einige Dinge zu beachten.
Zitieren von Shell- Variablen
Wenn Sie eine Variable in POSIX-Shells nicht in Anführungszeichen setzen (in Listenkontexten, wie in Argumenten für einen Befehl), ist dies nicht awk
der Operator split + glob.
Wenn Sie tun:
cmd foo=$var
Wo $var
ist * *
.
Bitten Sie die Shell nicht, den Inhalt $var
basierend auf dem Wert der $IFS
speziellen Shell-Variablen zu teilen , standardmäßig auf Leerzeichen. Das gibt uns also foo=*
und *
und führt Globbing für jeden von diesen durch, dh erweitert foo=*
auf alle Dateinamen im aktuellen Verzeichnis, die mit foo=
und *
auf alle nicht versteckten Dateinamen beginnen.
Sie sollten also fast immer Ihre Shell- Variablen zitieren , unabhängig davon, ob es sich um Argumente handelt awk
oder nicht. Dies gilt auch für die Shell-Befehlsersetzung ( `...`
und $(...)
) und die Shell-Arithmetik-Erweiterung ( $((...))
).
Daten so wie sie sind an übergeben awk
Das andere Problem ist, dass awk
(nicht die Shell) Backslash-Escape-Sequenzen in den Zuweisungen von Variablen wie -v var=value
(und mit GNU awk
4.2 oder höher, wenn der Wert mit beginnt @/
und endet /
, als regulärer Variablentyp behandelt wird ) erweitert wird.
Setzt beispielsweise -v var='\n/\n/'
den Inhalt der awk
var
Variablen auf <newline>/<newline>/
nicht \n/\n/
. Dies gilt auch für awk
Variablen, die definiert sind als:
awk '...' var=value
Um Daten zu übergeben, awk
ohne dass diese Erweiterung durchgeführt wird, können Sie die Arrays ENVIRON
oder ARGV
awk verwenden:
var=$value awk 'BEGIN {var=ENVIRON["var"]} ...'
(oben ist es eine Shell-Variablenzuweisung (zu einer Nicht-Array-Variablen), daher kann es keine Aufteilung + Glob geben, was einer der seltenen Fälle ist, in denen Sie die Anführungszeichen um Variablen weglassen können.)
oder:
awk 'BEGIN {var=ARGV[1]; delete ARGV[1]} ...' "$value"
Anführungszeichen und awk
Variablen
Dieser Split + Glob ist nur eine Shell- (Fehl-) Funktion. Die awk
Sprache ist eine ganz andere Sprache.
In awk
beziehen sich Variablen auf a varname
, not $varname
und Anführungszeichen werden verwendet, um Zeichenfolgen einzuführen. So "varname"
ist die varname
Zeichenfolge, während varname
auf die Variable verweist.
Bereinigen von Variablen, um Code-Injection zu vermeiden
Genau genommen ist das Zitieren von Shell-Variablen keine Bereinigung, es zitiert nicht die Variablen , die den Operator split + glob verwenden. Während Sie in den meisten Sprachen Anführungszeichen um feste Zeichenfolgen setzen, ist es in Shells umgekehrt: Alles ist Zeichenfolge, und Anführungszeichen werden verwendet, um ein bestimmtes Verhalten zu verhindern, und insbesondere Variablen sollten fast immer in Anführungszeichen gesetzt werden (eine schlechte Entwurfsentscheidung dieser Art) machte in der Bourne-Muschel in den 70er Jahren Sinn, ist aber ein Hindernis für moderne Muscheln, da zsh
es die einzige Muschel ist, die dies teilweise behoben hat.
Die Shell oder awk wertet / interpretiert keinen in ihrer eigenen Variablen gespeicherten Code aus, es sei denn, Sie weisen sie an.
var='foo; rm -f var'
echo $var
# or
echo "$var"
Bewirkt nicht, dass der Inhalt der Variablen als Shell-Code ausgewertet wird (obwohl der erste Code aufgeteilt und globalisiert wird, was schwerwiegende Folgen haben kann (z. B. mit var='/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*'
). Sie benötigen:
eval "echo $var"
# or
sh -c "echo $var"
damit es als Shell-Code ausgewertet / interpretiert wird.
awk
hat keine solche eval
Funktion. perl
Ich python
mache.
Achten Sie jedoch auf Kreuzkontaminationen. Sie können die Shell-Variablendaten (in Shell- Variablen) als Code ausführen lassen, um Folgendes auszuführen awk
:
awk '{print "'"$var"': " $0}'
wäre gefährlich, wenn die $var
Shell- Variable zum Beispiel enthält:
var='test"; print "foo" > /etc/passwd; print "blah'
weil die Shell dann ausführen würde:
["awk", "{print \"test\"; print \"foo\" > /etc/passwd; print \"blah: \" $0}"]
Oder umgekehrt:
awk '{system("echo foo: " $0)}' < file
Wo awk
würde eine Shell laufen als:
["sh", "-c", "echo foo: content-of-the-line"]
für jede Zeile von file
(und überlegen Sie, was eine Zeile wie ; rm -rf /
tun würde).
Es ist nicht nur zwischen awk
und sh
. Sie müssen vorsichtig sein, wenn variable / unkontrollierte Daten von einem anderen Interpreter als Code ausgewertet werden. Beispiele sind:
sed "s/$regexp/blah/g"
sed
's Sprache ist begrenzt, aber es kann immer noch schaden, wie bei regexp='//;w /etc/passwd; s/
'.
Oder:
find . -exec sh -c "echo {}" \;
Um diese Probleme zu vermeiden, gibt es zwei allgemeine Ansätze:
Konvertieren Sie die Variable von einem Interpreter in den anderen. Das funktioniert für die Shell -> awk oder find -> sh case oben. Wie Veränderung:
awk '{print "'"$var"': " $0}'
zu:
awk -v awk_var="$var" '{print awk_var ": " $0}'
Und:
find . -exec sh -c "echo {}" \;
zu:
find . -exec sh -c 'echo "$1"' sh {} \;
Aber das funktioniert nicht für die Shell -> sed oder awk -> Shell Fälle.
Wenn 1 nicht möglich ist, müssen Sie die Variablen bereinigen, um die möglicherweise problematischen Zeichen zu entfernen oder zu entfernen. Im,
awk '{system("echo foo: " $0)}'
Sie müssen in $0
etwas konvertieren , das für die Shell eine saubere Zeichenfolge ist. Eine Möglichkeit besteht darin, jedem Zeichen einen Backslash voranzustellen, dies funktioniert jedoch nicht für Zeilenumbrüche (hier kein Problem). Eine andere Möglichkeit besteht darin, die Zeichenfolge in einfache Anführungszeichen zu setzen und jedes einzelne Anführungszeichen zu umgehen.
awk 'function escape(s) {
gsub(/'\''/,"&\\\\&&",s)
return "'\''" s "'\''"
}
{system("echo foo: " escape($0))}'