Comment SSH peut-il fonctionner avec une condition if?

8

J'ai une ifdéclaration pour calculer les fichiers et supprimer tout sauf les trois derniers fichiers. Mais je veux exécuter cette commande à distance. Comment puis-je combiner sshavec une ifcondition?

J'ai essayé mais sans succès.

#!/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"

L'erreur que j'ai eue:

ls: impossible d'accéder à * .tgz: aucun fichier ou répertoire de ce type

Janith
la source
Alors, comment puis-je personnaliser cela?
Janith
@ user68186 Pourquoi ça? La commande est entre guillemets.
Courses de légèreté en orbite
2
La $( )partie de la commande est exécutée par le shell local avant même de démarrer la sshcommande. Cela est vrai à la fois lorsqu'il $( )est autonome et lorsqu'il est encadré par l' "art. Cependant, s'il $( )était à l'intérieur de 's, il ne serait pas exécuté par le shell local.
kasperd

Réponses:

3

REMARQUE: il y a en fait deux couches à la question ici. L'une est «Je veux exécuter une tâche non triviale sur un serveur distant accessible via SSH». L'autre est «J'essaie de passer une chaîne complexe à une commande, et l'argument finit par être différent de ce que je voulais». Je réponds à la question de bas niveau sans discuter si l'approche utilisée est "correcte" (pratique, sans risque d'erreur, sécurisée, etc.) pour résoudre la question de haut niveau. Comme indiqué par les autres réponses et commentaires, ce n'est probablement pas le cas.

Votre ligne de commande est généralement correcte; il suffit de changer un peu la citation.

Le problème principal est que les chaînes entre guillemets doubles sont développées par votre shell local, et donc les $(...)pièces seront évaluées sur votre système local. Pour les transmettre au système distant, vous devez placer le script entre guillemets simples.

Vous disposez également de guillemets intégrés. Dans votre script d'origine, il y a les arguments pour les deux echos; si vous changez la citation externe en guillemets simples, ce sera le script awk. Ces résultats entraînent la suppression des guillemets, ce qui ne dérange pas le echos, mais cela gâchera le script awk, car le signe supérieur à deviendra une redirection de sortie. Ainsi, après avoir modifié les guillemets externes en guillemets simples, changez-les en guillemets doubles.

Ceci est votre script avec la citation fixe. Le script peut avoir d'autres problèmes, je viens de corriger la syntaxe.

#!/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
la source
Je souhaite affecter /var/www/test.com/backupune variable appelée "BACKUPDEST" et cd $BACKUPDESTpouvez-vous me dire comment procéder?
Janith
1
Où attribuez-vous la valeur BACKUPDEST? Si cela se produit du côté distant, incluez-le simplement dans le script normalement. Si vous voulez le définir localement (par exemple le calculer dans le script local, ou le passer comme argument de ligne de commande), vous pouvez changer la première ligne par exemple "cd $BACKUPDEST"' ;- le shell étend la partie entre guillemets, conserve le guillemet simple partie intacte, concatène les deux et passe le résultat comme dernier argument à ssh.
pt314
Euh, faites ça "cd '$BACKUPDEST'"' ;au cas où le chemin dans cette variable contient un peu de bizarrerie (par exemple un espace). Encore une fois: dire simplement que cela peut être fait de cette façon, et non que cela devrait être fait de cette façon.
pt314
10

Oui, vous pouvez exécuter des scripts complexes via 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"

Cet exemple utilise un document bash here pour générer la chaîne de commande. Dans tous les cas, la transmission de scripts via ssh est sujette aux erreurs, car la citation et l'échappement des variables est difficile (faites attention à la barre oblique inverse avant les commandes). Si le script devient trop complexe, il est préférable de le copier via scppuis de l'exécuter sur l'hôte cible.

Je n'ai pas essayé de corriger votre script , mais voici un exemple sur la façon dont le comptage et la suppression sur un hôte distant pourraient fonctionner:

#!/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"

Le ls -t *.tgzne fonctionnera pas, car le globbing ne se passe sur le système local. L'utilisation lsde comptage de fichiers n'est pas non plus une bonne idée, car elle renvoie également des entrées comme ., ..et des répertoires.

Simon Sudler
la source
J'ai eu quelques erreurs. syntax error near unexpected token elif',elif [ $(ls -t |awk 'NR >3'|xargs rm -f) ]; then'
Janith
Il y avait un ;manquant dans la première instruction if. Mais je ne vous ai pas corrigé le script! Il y a beaucoup plus de problèmes, par exemple lels *.tgz
Simon Sudler
1
"Le ls -t * .tgz ne fonctionnera pas, car la globalisation ne se produit que sur le système local." - cela se produira sur le système distant si vous y échappez correctement. La version originale était nue à l' $(...)intérieur d'une chaîne entre guillemets doubles, donc le shell local l'a évaluée. L'échapper \$(...)comme vous l'avez montré dans le premier exemple, ou utiliser des guillemets simples dans tout le script, empêche le shell local de le développer, donc il est envoyé au système distant, il est développé par le shell distant et le script fonctionne comme prévu.
pt314
C'est le genre de situation où je recommanderais fortement d' utiliser des guillemets simples au lieu d'un document ici. Même si vous avez des variables à insérer, il vaut mieux utiliser une chaîne entre guillemets simples et printfdévelopper des variables - cela sera toujours plus facile à gérer que d'essayer d'échapper toutes les variables du shell. En outre, cela éviterait d'avoir à utiliser catsimplement pour capturer le document ici dans une variable!
Daniel Pryden
4

Je pense que tout ce truc de citation compliqué est une preuve suffisante pour ne pas l'utiliser mais utiliser un script à la place. Si vous souhaitez éviter plusieurs sshconnexions, dirigez le script vers l'autre hôte et laissez-le s'exécuter à partir d'une seule commande:

Fichier local, dites 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

Alors:

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

Ou (en évitant une utilisation inutile du chat ):

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

Cela myscript.shredirige le script local vers le côté distant où il est redirigé vers un fichier (temporaire) /tmp/ms.sh, exécuté et finalement supprimé.

Remarque: je n'ai pas vérifié le script d'origine pour les erreurs, mais je voulais juste montrer l'idée. Aucune citation sujette aux erreurs n'est nécessaire et toutes les commandes du script sont exécutées du côté distant.

PerlDuck
la source
Pas besoin non plus de sauvegarder le script dans un fichier temporaire, il suffit de le canaliser dans un shell approprié, c'est à dire ssh user@host bash <myscript.sh.
pt314
2
Gardez à l'esprit, cependant, que la redirection ne fonctionne que lorsque le script lui-même n'essaie pas de lire à partir de l'entrée standard.
chepner
@ pt314 Oui, mais voir Canaliser un script dans bash?
PerlDuck
Oui, la redirection a ses limites. Il évite également un tas de problèmes de sécurité associés à l'écriture (puis à l'exécution) d'un fichier temporaire nommé de manière prévisible. Si vous utilisez un script temporaire (que ce soit par choix ou par nécessité), créez-le à l'aide de mktempquelque chose de similaire.
pt314
3

Je préférerais placer le script sur l'instance distante et l'exécuter simplement via ssh, mais voici ma suggestion comment cela pourrait être fait comme vous le souhaitez:

#!/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

Remarques:

  • L'expression conditionnelle -ltest remplacée par -le.
  • eval - construire la commande en concaténant des arguments.
  • Je ne suis pas sûr que nous ayons vraiment besoin de ces citations supplémentaires \"dans l' $COMMAND{1..3}expression, mais je décide de les ajouter.
pa4080
la source