it-swarm.com.de

Erweiterung von Variablen in einfachen Anführungszeichen in einem Befehl in Bash

Ich möchte einen Befehl aus einem bash-Shell-Skript ausführen , das einfache Anführungszeichen und einige andere Befehle innerhalb der einfachen Anführungszeichen und eine Variable enthält.

z.B. repo forall -c '....$variable'

In diesem Format wird $ mit Escapezeichen versehen und die Variable wird nicht erweitert.

Ich habe folgende Variationen ausprobiert, diese wurden jedoch abgelehnt:

repo forall -c '...."$variable" '

repo forall -c " '....$variable' "

" repo forall -c '....$variable' "

repo forall -c "'" ....$variable "'"

Wenn ich den Wert anstelle der Variablen einsetze, wird der Befehl gut ausgeführt.

Bitte sag mir, wo ich falsch liege.

269
Rachit

In einfachen Zitaten bleibt ausnahmslos alles wörtlich erhalten.

Das bedeutet, Sie müssen die Anführungszeichen schließen, etwas einfügen und dann erneut eingeben.

'before'"$variable"'after'
'before'"'"'after'
'before'\''after'

Wie Sie überprüfen können, handelt es sich bei jeder der obigen Zeilen um ein einzelnes Word für die Shell. String-Verkettung wird einfach durch Nebeneinanderstellung durchgeführt. Anführungszeichen (einfache oder doppelte Anführungszeichen, abhängig von der Situation) werden verwendet, um die Interpretation verschiedener Sonderzeichen wie Whitespace, $, ;... zu deaktivieren. Auch relevant: Welche Zeichen müssen in bash entkommen werden?

Verketten Sie keine Zeichenfolgen, die von einer Shell interpretiert werden

Sie sollten absolut keine Shell-Befehle erstellen, indem Sie Variablen verketten. Dies ist eine schlechte Idee, die der Verkettung von SQL-Fragmenten ähnelt (SQL-Injection!).

Normalerweise ist es möglich, Platzhalter im Befehl zu haben und den Befehl zusammen mit Variablen anzugeben, damit der Aufseher sie aus der Liste der Aufrufargumente erhalten kann.

Zum Beispiel ist das Folgende sehr unsicher. Tun Sie das nicht

script="echo \"Argument 1 is: $myvar\""
/bin/sh -c "$script"

Wenn der Inhalt von $myvar nicht vertrauenswürdig ist, handelt es sich hier um einen Exploit:

myvar='foo"; echo "you were hacked'

Verwenden Sie anstelle des obigen Aufrufs Positionsargumente. Der folgende Aufruf ist besser - er ist nicht ausnutzbar:

script='echo "arg 1 is: $1"'
/bin/sh -c "$script" -- "$myvar"

Beachten Sie die Verwendung einzelner Häkchen in der Zuweisung zu script, was bedeutet, dass es wörtlich genommen wird, ohne dass eine Erweiterung der Variablen oder eine andere Interpretationsform erforderlich ist.

459
Jo So

Der Befehl repo kümmert sich nicht darum, welche Art von Anführungszeichen er erhält. Wenn Sie eine Parametererweiterung benötigen, verwenden Sie Anführungszeichen. Wenn das bedeutet, dass Sie eine Menge Backslash ausführen müssen, verwenden Sie einfache Anführungszeichen, und dann brechen Sie aus ihnen heraus und gehen Sie für den Teil, für den Sie die Erweiterung benötigen, in Doppelte.

repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff'

Erklärung folgt, wenn Sie interessiert sind.

Wenn Sie einen Befehl von der Shell aus ausführen, erhält der Befehl als Argument ein Array mit nullterminierten Zeichenfolgen. Diese Zeichenfolgen können absolut any nicht-null-Zeichen enthalten.

Wenn die Shell jedoch dieses String-Array von einer Befehlszeile aus erstellt, interpretiert sie einige Zeichen speziell. Dadurch sollen Befehle einfacher (sogar möglich) eingegeben werden können. Zum Beispiel geben Leerzeichen normalerweise die Grenze zwischen den Strings im Array an. Aus diesem Grund werden die einzelnen Argumente manchmal "Wörter" genannt. Ein Argument kann jedoch trotzdem Leerzeichen enthalten. Sie brauchen nur eine Möglichkeit, der Shell das zu sagen, was Sie wollen.

Sie können einen Backslash vor einem beliebigen Zeichen (einschließlich Leerzeichen oder einem anderen Backslash) verwenden, um der Shell mitzuteilen, dass dieses Zeichen wörtlich behandelt werden soll. Aber während Sie so etwas tun können:

echo \"Thank\ you.\ \ That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier

... es kann langweilig werden. Die Shell bietet also eine Alternative: Anführungszeichen. Diese gibt es in zwei Hauptvarianten.

Anführungszeichen werden als "Gruppierungsanführungszeichen" bezeichnet. Sie verhindern, dass Platzhalter und Aliasnamen erweitert werden. Meistens werden jedoch Leerzeichen in ein Word eingefügt. Andere Dinge wie die Parameter- und Befehlserweiterung (die Art von Dingen, die durch einen $ signalisiert werden) finden immer noch statt. Und wenn Sie in doppelte Anführungszeichen ein buchstäbliches Anführungszeichen setzen möchten, müssen Sie es mit einem Backslash versehen:

echo "\"Thank you. That'll be \$4.96, please,\" said the cashier"

Anführungszeichen sind eher drakonisch. Alles dazwischen wird wörtlich genommen, einschließlich Backslashes. Es gibt absolut keine Möglichkeit, ein einzelnes Anführungszeichen in einzelne Anführungszeichen zu setzen. 

Glücklicherweise sind Anführungszeichen in der Shell keine Word-Trennzeichen ; alleine beenden sie kein Wort. Sie können innerhalb eines Wortes in Anführungszeichen (oder zwischen verschiedenen Anführungszeichenarten) ein- und aussteigen, um das gewünschte Ergebnis zu erhalten: 

echo '"Thank you. That'\''ll be $4.96, please," said the cashier'

Das ist einfacher - viel weniger Backslashes, obwohl die Reihenfolge der einfachen Anführungszeichen, des umgekehrten Schrägstrichs, der einfachen Anführungszeichen und der Reihenfolge der einfachen Anführungszeichen gewöhnungsbedürftig ist.

Moderne Shells haben einen anderen, vom POSIX-Standard nicht angegebenen Anführungsstil hinzugefügt, bei dem dem führenden einfachen Anführungszeichen ein Dollarzeichen vorangestellt ist. So zitierte Strings folgen ähnlichen Konventionen wie String-Literale in der Programmiersprache ANSI C und werden daher manchmal als "ANSI-Strings" und das $'...'-Paar "ANSI-Anführungszeichen" bezeichnet. Innerhalb dieser Zeichenketten gilt der obige Hinweis, dass Backslashes wörtlich genommen werden, nicht mehr. Stattdessen werden sie wieder speziell - Sie können nicht nur ein wörtliches einfaches Anführungszeichen oder einen umgekehrten Schrägstrich einfügen, indem Sie einen umgekehrten Schrägstrich voranstellen, sondern die Shell erweitert auch die ANSI-C-Zeichenseiten (z. B. \n für eine neue Zeile, \t für Tabulator und \xHH für) das Zeichen mit Hexadezimalcode HH). Ansonsten verhalten sie sich jedoch als Strings mit einfachen Anführungszeichen: Es erfolgt keine Ersetzung von Parametern oder Befehlen:

echo $'"Thank you.  That\'ll be $4.96, please," said the cashier'

Es ist wichtig zu beachten, dass die einzelne Zeichenfolge, die als Argument für den Befehl echo empfangen wird, genau dieselbe in allen diesen Beispielen ist. Nachdem die Shell eine Befehlszeile analysiert hat, gibt es keine Möglichkeit, den Befehl auszuführen, um zu sagen, wie das Zitat gemacht wurde. Auch wenn es wollte.

76
Mark Reed

EDIT: (wie in den fraglichen Kommentaren :)

Ich habe mich seitdem damit beschäftigt. Ich hatte das Glück, dass ich Repo hatte. Es ist mir jedoch noch nicht klar, ob Sie Ihre Befehle zwingend in einfache Anführungszeichen setzen müssen. Ich habe mir die Repo-Syntax angesehen und glaube nicht, dass Sie das brauchen. Sie könnten doppelte Anführungszeichen für Ihren Befehl verwenden und dann die gewünschten einfachen und doppelten Anführungszeichen verwenden, sofern Sie doppelte Anführungszeichen verwenden.

2
Kevin Remo

Unten ist was für mich gearbeitet hat -

QUOTE="'"
Hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"
1
Manmohan

benutze einfach printf

anstatt 

repo forall -c '....$variable'

verwenden Sie printf, um das Variablentoken durch die erweiterte Variable zu ersetzen.

Zum Beispiel:

template='.... %s'

repo forall -c $(printf "${template}" "${variable}")
0
AndrewD

Variablen können einfache Anführungszeichen enthalten.

myvar=\'....$variable\'

repo forall -c $myvar
0
user3734617