GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Zitieren in Ssh $host $foo und Ssh $host „sudo Su User -c $foo“ Type Constructs?

Am Ende gebe ich oft komplexe Befehle über ssh aus; Diese Befehle beinhalten eine Weiterleitung an awk oder perl einzeilig und enthalten daher einfache Anführungszeichen und $’s. Ich konnte weder eine feste Regel finden, um das Zitieren richtig durchzuführen, noch eine gute Referenz dafür finden. Betrachten Sie beispielsweise Folgendes:

# what I'd run locally:
CMD='pgrep -fl java | grep -i datanode | awk '{print $1}'
# this works with ssh $host "$CMD":
CMD='pgrep -fl java | grep -i datanode | awk '"'"'{print $1}'"'"

(Beachten Sie die zusätzlichen Anführungszeichen in der awk-Anweisung.)

Aber wie bekomme ich das zum Laufen, z.B. ssh $host "sudo su user -c '$CMD'" ? Gibt es ein allgemeines Rezept für die Verwaltung von Angeboten in solchen Szenarien?..

Akzeptierte Antwort:

Der Umgang mit mehreren Zitationsebenen (eigentlich mehreren Parsing-/Interpretationsebenen) kann kompliziert werden. Es hilft, einige Dinge im Hinterkopf zu behalten:

  • Jede „Zitatebene“ kann möglicherweise eine andere Sprache beinhalten.
  • Zitierungsregeln variieren je nach Sprache.
  • Bei mehr als einer oder zwei verschachtelten Ebenen ist es normalerweise am einfachsten, „von unten nach oben“ zu arbeiten (d. h. von innen nach außen).

Zitierebenen

Sehen wir uns Ihre Beispielbefehle an.

pgrep -fl java | grep -i datanode | awk '{print $1}'

Ihr erster Beispielbefehl (oben) verwendet vier Sprachen:Ihre Shell, die Regex in pgrep , die Regex in grep (die sich möglicherweise von der Regex-Sprache in pgrep unterscheidet ) und awk . Es gibt zwei Interpretationsebenen:die Shell und eine Ebene nach der Shell für jeden der beteiligten Befehle. Es gibt nur eine explizite Zitierebene (Shell-Quoting in awk ).

ssh host …

Als Nächstes haben Sie eine Ebene von ssh hinzugefügt oben drauf. Dies ist praktisch eine weitere Shell-Ebene:ssh interpretiert den Befehl selbst nicht, sondern übergibt ihn an eine Shell am entfernten Ende (über (z. B.) sh -c …). ) und diese Shell interpretiert den String.

ssh host "sudo su user -c …"

Dann haben Sie gefragt, ob Sie mit su in der Mitte eine weitere Shell-Ebene hinzufügen möchten (über sudo , das seine Befehlsargumente nicht interpretiert, also können wir es ignorieren). An diesem Punkt haben Sie drei Verschachtelungsebenen (awk → Shell, Shell → Shell (ssh ), Shell → Shell (su user -c ), daher empfehle ich den „Bottom-Up“-Ansatz. Ich gehe davon aus, dass Ihre Shells Bourne-kompatibel sind (z. B. sh , Asche , Bindestrich , ksh , Bash , zsh , etc.). Eine andere Muschelart (Fisch , rc , usw.) erfordern möglicherweise eine andere Syntax, aber die Methode gilt immer noch.

Unten, oben

  1. Formulieren Sie die Zeichenfolge, die Sie auf der innersten Ebene darstellen möchten.
  2. Wählen Sie einen Zitiermechanismus aus dem Zitierrepertoire der nächsthöheren Sprache aus.
  3. Ziehen Sie die gewünschte Zeichenfolge gemäß Ihrem ausgewählten Zitiermechanismus in Anführungszeichen.
    • Es gibt oft viele Variationen, wie man welchen Zitiermechanismus anwendet. Das von Hand zu machen ist meist eine Frage der Übung und Erfahrung. Wenn Sie es programmatisch tun, ist es normalerweise am besten, die am einfachsten zu erreichende auszuwählen (normalerweise die „wörtlichsten“ (wenigsten Escapezeichen)).
  4. Verwenden Sie optional die resultierende Zeichenfolge in Anführungszeichen mit zusätzlichem Code.
  5. Wenn Sie das gewünschte Maß an Zitat/Interpretation noch nicht erreicht haben, nehmen Sie die resultierende Zeichenfolge in Anführungszeichen (plus ggf. hinzugefügten Code) und verwenden Sie sie als Startzeichenfolge in Schritt 2.

Die Semantik von Zitaten ist unterschiedlich

Dabei ist zu beachten, dass jede Sprache (Zitatebene) demselben Zitatzeichen eine leicht unterschiedliche Semantik (oder sogar eine drastisch unterschiedliche Semantik) verleihen kann.

Die meisten Sprachen haben einen „wörtlichen“ Zitiermechanismus, aber sie unterscheiden sich darin, wie wörtlich sie sind. Das einfache Anführungszeichen von Bourne-ähnlichen Shells ist eigentlich wörtlich (was bedeutet, dass Sie es nicht verwenden können, um ein einfaches Anführungszeichen selbst zu zitieren). Andere Sprachen (Perl, Ruby) sind weniger wörtlich, da sie einige interpretieren Backslash-Sequenzen innerhalb von Regionen in einfachen Anführungszeichen nicht wörtlich (insbesondere \ und ' ergibt und ' , aber andere Backslash-Sequenzen sind tatsächlich wörtlich).

Verwandte:Php:bcompiler_write_exe_footer — Schreibt die Startposition und die Signatur an das Ende einer Datei vom Typ exe

Sie müssen die Dokumentation für jede Ihrer Sprachen lesen, um die Zitierregeln und die allgemeine Syntax zu verstehen.

Ihr Beispiel

Die innerste Ebene Ihres Beispiels ist ein awk Programm.

{print $1}

Sie werden dies in eine Shell-Befehlszeile einbetten:

pgrep -fl java | grep -i datanode | awk …

Wir müssen (mindestens) das Leerzeichen und das $ schützen im awk Programm. Die offensichtliche Wahl besteht darin, in der Shell einfache Anführungszeichen um das gesamte Programm herum zu verwenden.

  • '{print $1}'

Es gibt jedoch noch andere Möglichkeiten:

  • {print $1} entkommen Sie direkt dem Leerzeichen und $
  • {print' $'1} einfaches Anführungszeichen nur das Leerzeichen und $
  • "{print $1}" doppelte Anführungszeichen für das Ganze und Escapezeichen für $
  • {print" $"1} doppelte Anführungszeichen nur das Leerzeichen und $
    Dies könnte die Regeln ein wenig beugen (unmaskiertes $ am Ende einer Zeichenfolge in doppelten Anführungszeichen ist wörtlich), aber es scheint in den meisten Shells zu funktionieren.

Wenn das Programm ein Komma zwischen den öffnenden und schließenden geschweiften Klammern verwenden würde, müssten wir auch entweder das Komma oder die geschweiften Klammern in Anführungszeichen setzen oder mit Escapezeichen versehen, um eine „geschweifte Klammererweiterung“ in einigen Shells zu vermeiden.

Wir wählen '{print $1}' und betten Sie es in den Rest des Shell-„Codes“ ein:

pgrep -fl java | grep -i datanode | awk '{print $1}'

Als nächstes wollten Sie dies über su ausführen und sudo .

sudo su user -c …

su user -c … ist genau wie some-shell -c … (außer unter einer anderen UID), also su fügt nur eine weitere Shell-Ebene hinzu. sudo interpretiert seine Argumente nicht, fügt also keine Zitatebenen hinzu.

Wir brauchen eine weitere Shell-Ebene für unseren Befehlsstring. Wir können wieder einfache Anführungszeichen auswählen, aber wir müssen die vorhandenen einfachen Anführungszeichen speziell behandeln. Der übliche Weg sieht so aus:

'pgrep -fl java | grep -i datanode | awk '''{print $1}''

Hier gibt es vier Zeichenfolgen, die die Shell interpretieren und verketten wird:die erste Zeichenfolge in einfachen Anführungszeichen (pgrep … awk ), ein einfaches Anführungszeichen mit Escapezeichen, das in einfache Anführungszeichen gesetzte awk Programm, ein weiteres einfaches Escape-Anführungszeichen.

Es gibt natürlich viele Alternativen:

  • pgrep -fl java | grep -i datanode | awk '{print $1} entkomme allem wichtigen
  • pgrep -fl java|grep -i datanode|awk '{print$1} das gleiche, aber ohne überflüssige Leerzeichen (sogar im awk Programm!)
  • "pgrep -fl java | grep -i datanode | awk '{print $1}'" das Ganze in doppelte Anführungszeichen setzen, das $ maskieren
  • 'pgrep -fl java | grep -i datanode | awk '"'"'{print $1}'"'" Ihre Variation; etwas länger als üblich, da doppelte Anführungszeichen (zwei Zeichen) anstelle von Escapezeichen (ein Zeichen) verwendet werden

Die Verwendung unterschiedlicher Zitate auf der ersten Ebene ermöglicht andere Variationen auf dieser Ebene:

  • 'pgrep -fl java | grep -i datanode | awk "{print $1}"'
  • 'pgrep -fl java | grep -i datanode | awk {print $1}'

Einbetten der ersten Variante in sudo /*su* Befehlszeile geben Sie Folgendes ein:

sudo su user -c 'pgrep -fl java | grep -i datanode | awk '''{print $1}''

Sie könnten dieselbe Zeichenfolge in jedem anderen Kontext auf Single-Shell-Ebene verwenden (z. B. ssh host … ).

Als Nächstes haben Sie eine Ebene von ssh hinzugefügt oben drauf. Dies ist praktisch eine weitere Shell-Ebene:ssh interpretiert den Befehl selbst nicht, sondern übergibt ihn an eine Shell auf der entfernten Seite (über (z. B.) sh -c …). ) und diese Shell interpretiert den String.

ssh host …

Der Vorgang ist derselbe:Nimm den String, wähle eine Zitiermethode, verwende ihn, bette ihn ein.

Wieder einfache Anführungszeichen verwenden:

'sudo su user -c '''pgrep -fl java | grep -i datanode | awk ''\'''{print $1}''\'

Jetzt gibt es elf Strings, die interpretiert und verkettet werden:'sudo su user -c ' , einfaches Escape-Anführungszeichen, 'pgrep … awk ' , einfaches Anführungszeichen mit Escapezeichen, umgekehrter Schrägstrich mit Escapezeichen, zwei einfache Anführungszeichen mit Escapezeichen, das einfache awk in Anführungszeichen Programm, ein einfaches Escape-Anführungszeichen, ein Escape-Backslash und ein abschließendes Escape-Anführungszeichen.

Verwandte:GNOME-Boxen booten von ISO nach der Installation eines Betriebssystems?

Das endgültige Formular sieht folgendermaßen aus:

ssh host 'sudo su user -c '''pgrep -fl java | grep -i datanode | awk ''\'''{print $1}''\'

Dies ist ein bisschen unhandlich, um es von Hand einzugeben, aber die wörtliche Natur der einfachen Anführungszeichen der Shell macht es einfach, eine leichte Variation zu automatisieren:

#!/bin/sh

sq() { # single quote for Bourne shell evaluation
    # Change ' to ''' and wrap in single quotes.
    # If original starts/ends with a single quote, creates useless
    # (but harmless) '' at beginning/end of result.
    printf '%sn' "$*" | sed -e "s/'/'\\''/g" -e 1s/^/'/ -e $s/$/'/
}

# Some shells (ksh, bash, zsh) can do something similar with %q, but
# the result may not be compatible with other shells (ksh uses $'...',
# but dash does not recognize it).
#
# sq() { printf %q "$*"; }

ap='{print $1}'
s1="pgrep -fl java | grep -i datanode | awk $(sq "$ap")"
s2="sudo su user -c $(sq "$s1")"

ssh host "$(sq "$s2")"

Linux
  1. Ist Gnome Terminal eine Art Non-Login-Shell?

  2. MySQL-Benutzer- und Datenbankgrundlagen

  3. Was ist der Unterschied zwischen /bin/false und /sbin/nologin als Shell des nologin-Benutzers?

  4. copy_to_user() und copy_from_user() für grundlegende Datentypen

  5. unterschiedliche Shell für Root- und Nicht-Root-Benutzer

Verwenden von Secure Shell (SSH) für die Anmeldung und Secure Copy (SCP) für die Datenübertragung unter Linux

Shell-Scripting Teil 2:Akzeptieren von Eingaben und Ausführen von Shell-Arithmetik

Linux-Grundlagen:So erstellen und installieren Sie SSH-Schlüssel auf der Shell

So beschränken Sie den SSH-Zugriff für Benutzer mit LShell (Limited Shell)

SSH-Tunneling und Proxying

Kann ich SSH Last-Login und MOTD für einzelne Benutzer deaktivieren?