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


393

Ich möchte einen Befehl aus einem Bash-Skript ausführen, das einfache Anführungszeichen und einige andere Befehle in einfachen Anführungszeichen und einer Variablen enthält.

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

In diesem Format $wird maskiert und die Variable wird nicht erweitert.

Ich habe die folgenden Variationen ausprobiert, aber sie wurden abgelehnt:

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

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

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

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

Wenn ich den Wert anstelle der Variablen ersetze, wird der Befehl einwandfrei ausgeführt.

Bitte sag mir, wo ich falsch liege.


33
repo forall -c ' ...before... '"$variable"' ...after...'
n. 'Pronomen' m.

2
@nm Die einfachen Anführungszeichen sind Teil des Repo-Befehls. Ich glaube nicht, dass dies funktionieren wird
Rachit

1
bashisst einfache Anführungszeichen. Entweder sind Sie nicht in bashoder einfache Anführungszeichen sind nicht Teil des repoBefehls.
n. 'Pronomen' m.

Ich bin mir nicht sicher, ob ich Sie verstehe, aber falls einfache Anführungszeichen vorhanden sein müssen, würden Sie nicht alle -c "'... vor ..." $ variable "... nach ...'" Arbeit repo?
ecv

Es ist ein Bash-Skript, das in der Bash-Shell ausgeführt wird, und der Befehl repo forall verwendet Parameter in einfachen Anführungszeichen. Wenn ich den tatsächlichen Wert ersetze, läuft der Befehl einwandfrei.
Rachit

Antworten:


621

In einfachen Anführungszeichen bleibt ausnahmslos alles buchstäblich erhalten.

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

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

Die Wortverkettung erfolgt einfach durch Nebeneinander. Wie Sie überprüfen können, ist jede der obigen Zeilen ein einzelnes Wort für die Shell. Anführungszeichen (einfache oder doppelte Anführungszeichen, je nach Situation) isolieren keine Wörter. Sie werden nur verwendet, um die Interpretation verschiedener Sonderzeichen wie Leerzeichen $, ;... zu deaktivieren. Ein gutes Tutorial zum Zitieren finden Sie in Mark Reeds Antwort. Ebenfalls relevant: Welche Zeichen müssen in Bash maskiert werden?

Verketten Sie keine von einer Shell interpretierten Zeichenfolgen

Sie sollten das Erstellen von Shell-Befehlen durch Verketten von Variablen unbedingt vermeiden. Dies ist eine schlechte Idee, ähnlich der Verkettung von SQL-Fragmenten (SQL-Injection!).

Normalerweise ist es möglich, Platzhalter im Befehl zu haben und den Befehl zusammen mit Variablen anzugeben, damit der Angerufene sie aus der Liste der Aufrufargumente empfangen 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 $myvarnicht vertrauenswürdig ist, ist hier ein Exploit:

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

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

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

Beachten Sie die Verwendung einzelner Häkchen in der Zuordnung zu script, was bedeutet, dass dies wörtlich genommen wird, ohne variable Erweiterung oder irgendeine andere Form der Interpretation.


@ Jo.SO das wird nicht funktionieren. weil der Befehl repo nur die Parameter akzeptiert, bis die ersten Anführungszeichen geschlossen sind. Alles, was danach passiert, wird für das Repo ignoriert und erzeugt einen weiteren Fehler.
Rachit

8
@Rachit - die Shell funktioniert so nicht. "some"thing' like'thisist alles ein Wort für die Shell. Anführungszeichen beenden nichts, was das Parsen betrifft, und es gibt keine Möglichkeit für einen Befehl, der von der Shell aufgerufen wird , zu sagen, was wie zitiert wurde.
Mark Reed

3
Dieser zweite Satz ("Sie können nicht einmal einfachen Anführungszeichen entkommen") macht einfache Anführungszeichen zu den schwarzen Löchern der Shell.

@Evert: Ich weiß nicht, wie ich es besser sagen soll. Ich habe den Satz entfernt.
Jo So

@JoSo Das ist eine Schande: Nein, der Witz fällt platt.

96

Dem repoBefehl ist es egal, welche Art von Anführungszeichen er erhält. Wenn Sie eine Parametererweiterung benötigen, verwenden Sie doppelte Anführungszeichen. Wenn dies bedeutet, dass Sie eine Menge Dinge rückgängig machen müssen, verwenden Sie für das meiste einfache Anführungszeichen und brechen Sie dann aus diesen heraus und gehen Sie für den Teil, in dem die Erweiterung stattfinden soll, in Doppel.

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 über die Shell ausführen, empfängt dieser Befehl als Argumente ein Array von nullterminierten Zeichenfolgen. Diese Zeichenfolgen können absolut jedes Nicht-Null-Zeichen enthalten.

Wenn die Shell dieses Array von Zeichenfolgen über eine Befehlszeile erstellt, werden einige Zeichen speziell interpretiert. Dies soll die Eingabe von Befehlen erleichtern (tatsächlich möglich machen). Beispielsweise geben Leerzeichen normalerweise die Grenze zwischen Zeichenfolgen im Array an. Aus diesem Grund werden die einzelnen Argumente manchmal als "Wörter" bezeichnet. Ein Argument kann jedoch Leerzeichen enthalten. Sie brauchen nur eine Möglichkeit, der Shell zu sagen, dass Sie das wollen.

Sie können einen Backslash vor einem beliebigen Zeichen (einschließlich Leerzeichen oder einem anderen Backslash) verwenden, um die Shell anzuweisen, dieses Zeichen wörtlich zu behandeln. Aber während Sie so etwas tun können:

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

... es kann lästig werden. Die Shell bietet also eine Alternative: Anführungszeichen. Diese kommen in zwei Hauptsorten.

Doppelte Anführungszeichen werden als "Gruppieren von Anführungszeichen" bezeichnet. Sie verhindern, dass Platzhalter und Aliase erweitert werden, aber meistens dienen sie dazu, Leerzeichen in ein Wort aufzunehmen. Andere Dinge wie die Parameter- und Befehlserweiterung (die Art von Dingen, die durch a signalisiert werden $) passieren immer noch. Und wenn Sie ein wörtliches doppeltes Anführungszeichen in doppelten Anführungszeichen haben möchten, müssen Sie es natürlich umkehren:

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

Einfache Anführungszeichen sind drakonischer. Alles zwischen ihnen wird buchstäblich genommen, einschließlich Backslashes. Es gibt absolut keine Möglichkeit, ein wörtliches einfaches Anführungszeichen in einfache Anführungszeichen zu setzen.

Glücklicherweise sind Anführungszeichen in der Shell keine Worttrennzeichen . an sich beenden sie kein Wort. Sie können innerhalb desselben Wortes in Anführungszeichen ein- und ausgehen, auch zwischen verschiedenen Arten von Anführungszeichen, um das gewünschte Ergebnis zu erzielen:

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

Das ist also einfacher - viel weniger Backslashes, obwohl die Sequenz "Close-Single-Quote", "Backslashed-Literal-Single-Quote", "Open-Single-Quote" gewöhnungsbedürftig ist.

Moderne Shells haben einen weiteren Anführungszeichenstil hinzugefügt, der nicht im POSIX-Standard festgelegt ist. Dem führenden einfachen Anführungszeichen wird ein Dollarzeichen vorangestellt. So zitierte Zeichenfolgen folgen ähnlichen Konventionen wie Zeichenfolgenliterale in der Programmiersprache ANSI C und werden daher manchmal als "ANSI-Zeichenfolgen" und das $'... 'Paar "ANSI-Anführungszeichen" bezeichnet. Innerhalb solcher Zeichenfolgen gilt der obige Hinweis, dass Backslashes buchstäblich verwendet werden, nicht mehr. Stattdessen werden sie wieder zu etwas Besonderem. Sie können nicht nur ein wörtliches einfaches Anführungszeichen oder einen umgekehrten Schrägstrich einfügen, indem Sie ihm einen umgekehrten Schrägstrich voranstellen, sondern die Shell erweitert auch die \nEscapezeichen \tfür ANSI C-Zeichen (z. B. für eine neue Zeile, für Tabulatoren und \xHHfür das Zeichen mit HexadezimalcodeHH). Andernfalls verhalten sie sich jedoch wie Zeichenfolgen in einfachen Anführungszeichen: Es findet keine Parameter- oder Befehlsersetzung statt:

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 echoBefehl empfangen wird , in all diesen Beispielen genau dieselbe ist . Nachdem die Shell eine Befehlszeile analysiert hat, kann der ausgeführte Befehl nicht mehr angeben, was wie angegeben wurde. Auch wenn es wollte.


2
Ja. Ich verstehe jetzt. Es funktioniert perfekt. Vielen dank für Deine Hilfe!
Rachit

6
Diese Antwort war großartig.
Vinícius Ferrão

1
Ist das $'string'Format POSIX kompatibel? Gibt es auch einen Namen dafür?
Wildcard

2
@Wildcard $'string'ist eine Nicht-POSIX-Erweiterung, die ich als "ANSI-Zeichenfolgen" bezeichnet habe. Ich habe diese Fakten in die Antwort aufgenommen. Solche Zeichenfolgen funktionieren in den meisten modernen Bourne-kompatiblen Shells: bash, dash, ksh (sowohl AT & T als auch PD) und zsh unterstützen sie alle.
Mark Reed

1
Link zurück - Welche Zeichen müssen in Befehlszeilenargumenten maskiert werden? auf dem Unix & Linux Stack Exchange.
Wildcard

3

Unten ist, was für mich funktioniert hat -

QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"

2
Ich hoffe, TBL_HDFS_DIR_PATH ist übrigens keine vom Benutzer bereitgestellte Eingabe, dies würde eine schöne SQL-Injektion ermöglichen ...
Domenukk

1
Das Einfügen QUOTEeiner separaten Variablen ist völlig überflüssig. einfache Anführungszeichen in doppelten Anführungszeichen sind nur reguläre Zeichen. (Verwenden Sie auch keine Großbuchstaben für Ihre privaten Variablen.)
Tripleee

2

EDIT: (Gemäß den fraglichen Kommentaren :)

Ich habe mich seitdem damit befasst. Ich hatte das Glück, dass ich Repo herumliegen hatte. Trotzdem ist mir nicht klar, ob Sie Ihre Befehle mit Gewalt in einfache Anführungszeichen setzen müssen. Ich habe mir die Repo-Syntax angesehen und glaube nicht, dass Sie das brauchen. Sie können doppelte Anführungszeichen um Ihren Befehl verwenden und dann alle einfachen und doppelten Anführungszeichen verwenden, die Sie benötigen, vorausgesetzt, Sie entkommen doppelten Anführungszeichen.


Danke Kevin für deine Hilfe.
Rachit

2

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}")

Das ist kaputt; printfkauft Ihnen hier nicht wirklich etwas, und wenn Sie die Befehlssubstitution nicht zitieren, entstehen neue Anführungsprobleme.
Tripleee

0

Variablen können einfache Anführungszeichen enthalten.

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

repo forall -c $myvar

Sie können, aber dies ist nicht der richtige Weg - Sie sollten immer noch "$myvar"doppelte Anführungszeichen setzen.
Tripleee

-2

Geht das für dich?

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

8
Tut es für dich? Diese Frage ist fünf Jahre alt und hat bereits einige Antworten, sogar eine akzeptierte ...
Nico Haase
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.