Wie kann SSH mit einer if-Bedingung arbeiten?

8

Ich habe eine ifAnweisung Dateien zu berechnen und alle außer spätestens drei Dateien zu löschen. Ich möchte diesen Befehl jedoch remote ausführen. Wie kann ich sshmit einer ifBedingung kombinieren ?

Ich habe es versucht, aber keinen Erfolg.

#!/bin/bash

ssh -t  test@192.168.94.139 "cd /var/www/test.com/backup ;

if [ $(ls  | wc -l) -lt 3  ]
then
    echo "Less"
else [ $(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]
    echo "deleted"
fi"

Der Fehler, den ich bekam:

ls: kann nicht auf * .tgz zugreifen: Keine solche Datei oder kein solches Verzeichnis

Janith
quelle
Wie kann ich das dann anpassen?
Janith
@ user68186 Warum ist das so? Der Befehl steht in Anführungszeichen.
Leichtigkeitsrennen im Orbit
2
Der $( )Teil des Befehls wird von der lokalen Shell ausgeführt, bevor der sshBefehl überhaupt gestartet wird . Das gilt sowohl für $( )sich allein als auch für "s. Wenn es sich $( )jedoch in 's befindet, wird es nicht von der lokalen Shell ausgeführt.
Kasperd

Antworten:

3

HINWEIS: Die Frage besteht aus zwei Ebenen. Eine lautet: "Ich möchte eine nicht triviale Aufgabe auf einem Remote-Server ausführen, auf den über SSH zugegriffen werden kann." Das andere ist: "Ich versuche, eine komplexe Zeichenfolge an einen Befehl zu übergeben, und das Argument unterscheidet sich letztendlich von dem, was ich beabsichtigt habe." Ich beantworte die Frage auf niedriger Ebene, ohne zu diskutieren, ob der verwendete Ansatz "richtig" ist (praktisch, nicht fehleranfällig, sicher usw.), um die Frage auf hoher Ebene zu lösen. Wie aus den anderen Antworten und Kommentaren hervorgeht, ist dies möglicherweise nicht der Fall.

Ihre Befehlszeile ist größtenteils richtig. Sie müssen das Zitat nur ein wenig ändern.

Das Hauptproblem besteht darin, dass Zeichenfolgen in doppelten Anführungszeichen von Ihrer lokalen Shell erweitert werden und die $(...)Teile daher auf Ihrem lokalen System ausgewertet werden. Um sie an das Remote-System weiterzuleiten, müssen Sie das Skript in einfache Anführungszeichen setzen.

Sie haben auch einige eingebettete Anführungszeichen. In Ihrem ursprünglichen Skript gibt es die Argumente für die beiden echos; Wenn Sie das äußere Anführungszeichen in einfache Anführungszeichen ändern, handelt es sich um das awk-Skript. Diese führen effektiv dazu, dass die Anführungszeichen weggelassen werden, was das echos nicht stört , aber das awk-Skript durcheinander bringt, da das Größer-als-Zeichen zur Ausgabeumleitung wird. Nachdem Sie die äußeren Anführungszeichen in einfache Anführungszeichen geändert haben, ändern Sie diese in doppelte Anführungszeichen.

Dies ist Ihr Skript mit festem Zitat. Das Skript kann andere Probleme haben, ich habe nur die Syntax behoben.

#!/bin/bash

ssh -t  test@192.168.94.139 'cd /var/www/test.com/backup ;

if [ $(ls  | wc -l) -lt 3  ]
then
  echo "Less"
else [ $(ls -t *.tgz|awk "NR >3"|xargs rm -f) ]
  echo "deleted"
fi'
pt314
quelle
Ich möchte /var/www/test.com/backupeine Variable namens "BACKUPDEST" zuweisen und cd $BACKUPDESTkönnen Sie mir sagen, wie das geht?
Janith
1
Wo weisen Sie den Wert zu BACKUPDEST? Wenn dies auf der Remote-Seite geschieht, fügen Sie es normalerweise normal in das Skript ein. Wenn Sie es lokal (zB berechnen sie im lokalen Skript oder es in als Kommandozeilen - Argument übergeben) festlegen möchten, können Sie die erste Zeile zB ändern "cd $BACKUPDEST"' ;- die Shell den doppelten Anführungszeichen Teil erweitert wird , hält die einfachen Anführungszeichen Teil intakt, verkettet die beiden und übergibt das Ergebnis als letztes Argument an ssh.
Pt314
Stellen Sie sicher, dass "cd '$BACKUPDEST'"' ;der Pfad in dieser Variablen eine gewisse Verrücktheit enthält (z. B. ein Leerzeichen). Nochmals: Ich sage nur, dass es so gemacht werden kann, nicht, dass es so gemacht werden sollte.
Pt314
10

Ja, Sie können komplexe Skripte über ausführen ssh

#!/bin/bash -e

ssh_cmd="$(cat <<-EOF
    cd /var/www/test.com/backup;

    if [ \$(ls  | wc -l) -lt 3  ]; then
        echo "less"
    elif [ \$(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]; then
        echo "deleted"
    else
        echo "something else"
    fi
EOF
)"

ssh -t test@192.168.94.139 "$ssh_cmd"

In diesem Beispiel wird ein Bash- Here-Dokument verwendet , um die Befehlszeichenfolge zu generieren. In jedem Fall ist das Übergeben von Skripten über ssh fehleranfällig, da das Zitieren und Escapezeichen von Variablen schwierig ist (beachten Sie den Backslash vor den Befehlen). Wenn das Skript zu komplex wird, ist es besser, es über zu kopieren scpund dann auf dem Zielhost auszuführen.

Ich habe nicht versucht, Ihr Skript zu reparieren , aber hier ist ein Beispiel dafür, wie das Zählen und Löschen auf einem Remote-Host funktionieren kann:

#!/bin/bash -e

tmp_dir="$(mktemp -d)"

ssh_cmd="$(cat <<-EOF
    cd "$tmp_dir"

    cnt_tgz=(\$(find . -type f -name "*.tgz"))
    if [[ \${#cnt_tgz[@]} -lt 3 ]]; then
        echo "less"
    else
        rm "\${cnt_tgz[@]}"
        echo "deleted"
    fi
EOF
)"

touch "$tmp_dir/1.tgz"
ssh -t localhost "$ssh_cmd"
touch "$tmp_dir/2.tgz" "$tmp_dir/3.tgz"
ssh -t localhost "$ssh_cmd"

Das ls -t *.tgzwird nicht funktionieren, da das Globbing nur auf dem lokalen System stattfindet. Auch unter Verwendung von lsDateien für das Zählen ist keine gute Idee, da es auch wieder Einträge wie ., ..und Verzeichnisse.

Simon Sudler
quelle
Ich habe einige Fehler bekommen. syntax error near unexpected token elif',elif [ $(ls -t |awk 'NR >3'|xargs rm -f) ]; then'
Janith
;In der ersten if-Anweisung fehlte ein . Aber ich habe dein Skript nicht repariert! Es gibt viele weitere Probleme, z. B. diels *.tgz
Simon Sudler
1
"Das ls -t * .tgz funktioniert nicht, da das Globbing nur auf dem lokalen System stattfindet." - Es passiert auf dem Remote-System, wenn Sie es ordnungsgemäß verlassen. Die Originalversion war nackt $(...)in einer Zeichenfolge in doppelten Anführungszeichen, daher wurde sie von der lokalen Shell ausgewertet. Wenn Sie es wie \$(...)im ersten Beispiel gezeigt maskieren oder einfache Anführungszeichen für das gesamte Skript verwenden, wird verhindert, dass die lokale Shell es erweitert, sodass es an das Remote-System gesendet wird, von der Remote-Shell erweitert wird und das Skript funktioniert wie beabsichtigt.
Pt314
Dies ist die Art von Situation, in der ich die Verwendung von einfachen Anführungszeichen anstelle eines hier gezeigten Dokuments sehr empfehlen würde . Selbst wenn Sie einige Variablen einfügen möchten, ist es besser, eine Zeichenfolge printfin einfachen Anführungszeichen zu verwenden und Variablen zu erweitern. Dies ist immer noch einfacher zu verwalten, als zu versuchen, alle Shell-Variablen durchgehend zu umgehen. Außerdem müsste vermieden werden, dass catnur das Dokument hier in einer Variablen erfasst wird!
Daniel Pryden
4

Ich denke, dieses ganze komplizierte Zitiermaterial ist Beweis genug, um es nicht zu verwenden, sondern stattdessen ein Skript zu verwenden. Wenn Sie mehrere sshVerbindungen vermeiden möchten, leiten Sie das Skript an den anderen Host weiter und lassen Sie es dort mit einem Befehl ausführen:

Lokale Datei, sagen Sie myscript.sh:

cd /var/www/test.com/backup;

if [ $(ls  | wc -l) -lt 3  ]; then
    echo "less"
elif [ $(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]; then
    echo "deleted"
else
    echo "something else"
fi

Dann:

cat myscript.sh | \
    ssh -t test@192.168.94.139 \
    "cat - > /tmp/ms.sh && sh /tmp/ms.sh && rm /tmp/ms.sh"

Oder (Vermeidung einen nutzlosen Einsatz von Katze ):

ssh -t test@192.168.94.139 \
    "cat - > /tmp/ms.sh && sh /tmp/ms.sh && rm /tmp/ms.sh" \
    < myscript.sh

Dadurch wird das lokale Skript myscript.shan die Remote-Seite weitergeleitet, wo es in eine (temporäre) Datei umgeleitet /tmp/ms.sh, ausgeführt und schließlich entfernt wird.

Hinweis: Ich habe das ursprüngliche Skript nicht auf Fehler überprüft, sondern wollte nur die Idee zeigen. Es ist kein fehleranfälliges Zitieren erforderlich, und alle Befehle im Skript werden auf der Remote-Seite ausgeführt.

PerlDuck
quelle
Sie müssen das Skript auch nicht in einer temporären Datei speichern, sondern müssen es nur in eine geeignete Shell leiten, d ssh user@host bash <myscript.sh. H.
Pt314
2
Beachten Sie jedoch, dass die Umleitung nur funktioniert, wenn das Skript selbst nicht versucht, aus der Standardeingabe zu lesen.
Chepner
@ pt314 Ja, aber siehe Pipe ein Skript in Bash?
PerlDuck
Ja, die Umleitung hat ihre Grenzen. Außerdem werden eine Reihe von Sicherheitsproblemen vermieden, die mit dem Schreiben (und anschließenden Ausführen) einer temporären Datei mit vorhersehbarem Namen verbunden sind. Wenn Sie ein temporäres Skript verwenden (ob nach Wahl oder Notwendigkeit), erstellen Sie es mit mktempoder ähnlich sicher.
Pt314
3

Ich würde es vorziehen, das Skript auf der Remote-Instanz zu platzieren und es einfach über ssh auszuführen, aber hier ist mein Vorschlag, wie dies auf die von Ihnen gewünschte Weise geschehen könnte:

#!/bin/bash

HOST='[email protected]'
REMOTE_PATH='/var/www/test.com/backup'

COMMAND1="ssh \"$HOST\" 'ls \"$REMOTE_PATH\" | wc -l'"
COMMAND2="ssh \"$HOST\" 'ls -t \"$REMOTE_PATH\"/*.tgz'"
COMMAND3="xargs ssh \"$HOST\" rm -rf"

if [[ $(eval "$COMMAND1") -le 3 ]]
then
    echo "Less"
else
    eval "$COMMAND2" | awk 'NR > 3' | eval "$COMMAND3" && echo "Deleted"
fi

Anmerkungen:

  • Der bedingte Ausdruck -ltwird durch ersetzt -le.
  • eval - Befehl konstruieren durch Verketten von Argumenten.
  • Ich bin mir nicht sicher, ob wir diese zusätzlichen Zitate \"im $COMMAND{1..3}Ausdruck wirklich brauchen , aber ich beschließe, sie hinzuzufügen.
pa4080
quelle