¿Cómo puede funcionar SSH con una condición if?

8

Tengo una ifdeclaración para calcular archivos y eliminar todos excepto los últimos tres archivos. Pero quiero ejecutar este comando de forma remota. ¿Cómo puedo combinar sshcon una ifcondición?

Intenté esto pero no tuve éxito.

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

El error que obtuve:

ls: no se puede acceder a * .tgz: no existe tal archivo o directorio

Janith
fuente
Entonces, ¿cómo puedo personalizar esto?
Janith
@ user68186 ¿Por qué es eso? El comando está entre comillas.
Carreras de ligereza en órbita el
2
El $( )shell local ejecuta la parte del sshcomando incluso antes de que comience el comando. Eso es cierto tanto cuando $( )está solo como cuando está encerrado por "s. Sin embargo, si $( )estuviera dentro de 's, no sería ejecutado por el shell local.
kasperd

Respuestas:

3

NOTA: de hecho, hay dos capas para la pregunta aquí. Una es 'Quiero ejecutar una tarea no trivial en un servidor remoto accesible a través de SSH'. La otra es "Estoy tratando de pasar una cadena compleja a un comando, y el argumento termina siendo diferente de lo que pretendía". Estoy respondiendo la pregunta de bajo nivel sin discutir si el enfoque utilizado es "correcto" (conveniente, no propenso a errores, seguro, etc.) para resolver el de alto nivel. Como lo indican las otras respuestas y comentarios, posiblemente no lo sea.

Su línea de comando es mayormente correcta; solo tienes que cambiar un poco la cita.

El problema principal es que las cadenas de comillas dobles son expandidas por su shell local, por lo que las $(...)partes se evaluarán en su sistema local. Para pasarlos al sistema remoto, debe encerrar el script entre comillas simples.

También tiene algunas comillas incrustadas. En su guión original, hay argumentos para los dos echos; si cambia la cita externa a comillas simples, será la secuencia de comandos awk. Estos resultados resultan en la omisión de las comillas, lo que no molesta a la echos, pero desordenará el script awk, ya que el signo mayor que se convertirá en redirección de salida. Entonces, después de cambiar las comillas externas a comillas simples, cámbielas a comillas dobles.

Este es su script con la cita fija. El script puede tener otros problemas, solo arreglé la sintaxis.

#!/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
fuente
Quiero asignar /var/www/test.com/backupa una variable llamada "BACKUPDEST" y cd $BACKUPDEST¿Me puede decir cómo hacerlo?
Janith
1
¿A dónde asignas el valor BACKUPDEST? Si eso sucede en el lado remoto, simplemente inclúyalo en el script normalmente. Si desea configurarlo localmente (por ejemplo, calcularlo en el script local o pasarlo como un argumento de línea de comando), puede cambiar la primera línea a, por ejemplo "cd $BACKUPDEST"' ;, el shell expande la parte entre comillas dobles, mantiene la comilla simple parte intacta, concatena los dos y pasa el resultado como último argumento a ssh.
pt314
Uh, haz que "cd '$BACKUPDEST'"' ;en caso de que la ruta en esa variable contenga algo extraño (por ejemplo, un espacio). De nuevo: solo digo que se puede hacer de esta manera, no que se debe hacer de esta manera.
pt314
10

Sí, puede exucir scripts complejos a través de 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"

Este ejemplo utiliza un documento bash here para generar la cadena de comando. En cualquier caso, pasar scripts a través de ssh es propenso a errores, porque es difícil citar y escapar de las variables (tenga en cuenta la barra invertida antes de los comandos). Si el script se vuelve demasiado complejo, es mejor copiarlo scpy luego ejecutarlo en el host de destino.

No intenté arreglar su script , pero aquí hay un ejemplo de cómo podría funcionar contar y eliminar en un host remoto:

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

El ls -t *.tgzno va a funcionar, ya que el englobamiento está sucediendo solamente en el sistema local. Tampoco lses una buena idea usarlo para contar archivos, ya que también devuelve entradas como ., ..y directorios.

Simon Sudler
fuente
Tengo algunos errores syntax error near unexpected token elif',elif [ $(ls -t |awk 'NR >3'|xargs rm -f) ]; then'
Janith
Había una ;falta en el puño si-declaración. ¡Pero no te arreglé el script! Hay muchos más problemas, por ejemplo, ells *.tgz
Simon Sudler
1
"El ls -t * .tgz no funcionará, ya que el bloqueo solo está ocurriendo en el sistema local". - Sucederá en el sistema remoto si se escapa correctamente. La versión original estaba desnuda $(...)dentro de una cadena entre comillas dobles, por lo que el shell local la evaluó. Escaparlo \$(...)como se mostró en el primer ejemplo, o usar comillas simples alrededor de todo el script, evita que el shell local lo expanda, por lo que se envía al sistema remoto, el shell remoto lo expande y el script funciona Como era la intención.
pt314
Este es el tipo de situación en la que yo altamente recomendar el uso de comillas simples en lugar de un documento aquí. Incluso si tiene algunas variables que desea insertar, es mejor usar una cadena entre comillas simples y printfexpandir las variables; aún así será más fácil de administrar que tratar de escapar de todas las variables de shell. ¡Además, evitaría la necesidad de usar catsolo para capturar el documento aquí en una variable!
Daniel Pryden
4

Creo que todo este complicado asunto de citas es prueba suficiente para no usarlo, sino usar un script. Si desea evitar varias sshconexiones, canalice la secuencia de comandos al otro host y deje que se ejecute allí en un comando:

Archivo local, diga 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

Entonces:

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

O (evitando un uso inútil del gato ):

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

Esto canaliza la secuencia myscript.shde comandos local al lado remoto donde se redirige a un archivo (temporal) /tmp/ms.sh, se ejecuta y finalmente se elimina.

Nota: no verifiqué el script original en busca de errores, pero solo quería mostrar la idea. No es necesaria una cita propensa a errores y todos los comandos en el script se ejecutan en el lado remoto.

PerlDuck
fuente
Tampoco es necesario guardar el script en un archivo temporal, simplemente canalizarlo en un shell adecuado, es decir ssh user@host bash <myscript.sh.
pt314
2
Sin embargo, tenga en cuenta que la redirección solo funciona cuando el script en sí no intenta leer desde la entrada estándar.
chepner
@ pt314 Sí, pero ¿ves Pipe un script en bash?
PerlDuck
Sí, la redirección tiene sus limitaciones. También evita un montón de problemas de seguridad asociados con la escritura (y luego la ejecución) de un archivo temporal con un nombre predecible. Si usa un script temporal (ya sea por elección o necesidad), créelo usando mktempo algo similarmente seguro.
pt314
3

Preferiría colocar el script en la instancia remota y simplemente ejecutarlo a través de ssh, pero aquí está mi sugerencia de cómo se podría hacer de la manera que desee:

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

Notas:

  • La expresión condicional -ltse reemplaza por -le.
  • eval - Construir comandos concatenando argumentos.
  • No estoy seguro de si realmente necesitamos estas citas adicionales \"dentro de la $COMMAND{1..3}expresión, pero decido agregarlas.
pa4080
fuente