2008-12-06 28 views
25

He estado golpeando mi cabeza contra la pared por un tiempo con esta.Bash while read loop breaking early

Quiero SSH en un conjunto de máquinas y verificar si están disponibles (aceptando conexiones y no usándose). He creado un pequeño script, tssh, que hace precisamente eso:

#!/bin/bash 

host=$1 
timeout=${2:-1} 

ssh -qo "ConnectTimeout $timeout" $host "[ \`who | cut -f1 | wc -l \` -eq 0 ] && exit 0 || exit 1" 

Este script funciona correctamente. Devolviendo 255 si hubo un problema de conexión, 1 si la máquina está ocupada y 0 si todo está bien. Si alguien conoce una mejor manera de hacerlo, por favor avíseme.

A continuación, intento llamar a tssh en mi conjunto de máquinas utilizando un ciclo while read, y aquí es donde todo sale mal. El ciclo se cierra tan pronto como tssh regrese a 0 y nunca complete el conjunto completo.

while read nu ; do tssh "MYBOXES$nu" ; done < <(ruby -e '(0..20).each { |i| puts i }') 

Al principio pensé que esto era un problema de subshell pero aparentemente no. ¡Cualquier ayuda, junto con comentarios sobre el estilo/contenido, sería muy apreciada! sé que voy a patear a mí mismo cuando me entero de qué ...

+0

estoy teniendo un problema similar con la red de Perl :: SSH :: Perl, @ Foo-Bah ha descrito el problema muy bien, y la solución es añadir una cadena vacía como parámetro al cmd: https://metacpan.org/pod/Net::SSH::Perl#out-err-exit-ssh-cmd-cmd-stdin my ($ stdout, $ stderr, $ exit) = $ ssh-> cmd ($ cmd, ""); – Thomas

Respuesta

4

No sabe si ayudaría, pero una forma más limpia de la escritura que habría

for nu in `ruby -e '(0..20).each { |i| puts i}'`; do 
    tssh "MYBOXES$nu" 
done 
+3

Además, si tiene GNU Coreutils, puede usar 'seq 0 20' en lugar del comando ruby. –

+0

@Paul ¡Gracias, eso funciona! ¡Ahora si solo supiera por qué! Tal vez es un problema subshell después de todo. Ciertamente me recuerda los problemas previos de subshell que tuve. @Jouni He sustituido la parte de ruby ​​fea con seq. No estaba enterado de ese comando, gracias. @Todo ¿Alguna idea de por qué funciona esta solución? –

+1

@Paul Es un problema ssh. Haré una publicación –

3

estoy también seguro acerca de por qué no funciona, pero me gusta xargs y seq:

seq 0 20 | xargs -n1 tssh MYBOXES 
2

no puedo creer que era el resultado de 0 que rompió el lazo, se puede comprobar con respecto a este mediante la sustitución de su comando tssh en el bucle con "/bin/true "que también devuelve 0.

con respecto al estilo no entiendo por qué una secuencia de comandos de shell con bucle simple necesita ruby, perl, seq o jot o cualquier otro binario que no esté en mi * BSD.

también, puede utilizar las conchas incorporadas para construcción de bucle, que funciona al menos en ksh, bash:

for ((i=0; $i<=20; i++)); do 
    tssh "MYBOXES$i" 
done 
6

me encontré con esto hoy - rsh y/o ssh puede romper un tiempo debido leer bucle a ella usando stdin. Puse una -n en la línea ssh que evita que intente usar stdin y solucionó el problema.

3

Como mencionó Kaii, es realmente excesivo llamar a ruby ​​o seq (que no funcionará en las máquinas BSD u OSX) solo para generar un rango de números. Si está satisfecho con el uso de fiesta se puede:

for i in {0..20}; do 
    # command 
done 

Creo que esto debería funcionar para 2.05b bash y hacia arriba.

36

Chris es correcto. La fuente del rompimiento del bucle fue SSH usando stdin, sin embargo, las armas son correctas en el uso de una metodología de bucle.

Si realiza un bucle a través de la entrada (un archivo con una lista de nombres de host, por ejemplo) y llama a SSH, necesita pasar el parámetro -n; de lo contrario, su bucle basado en la entrada fallará.

while read host; do 
    ssh -n $host "remote command" >> output.txt 
done << host_list_file.txt 
2

hablar sobre un problema de "robar Peter a pagar Paul". Había estado luchando durante horas para descubrir por qué mi ssh estaba matando mi algo | while read loop.

Otra forma de seguir con un ciclo de lectura while y mantener su ssh al mismo tiempo es usar el interruptor "-n" para hacer STDIN en ssh/dev/null. Funciona como un encanto para mí:

#!/bin/bash 
[...] 
something|while read host 
    do 
     ssh -nx ${host} fiddleAround 
    done 

(. Tiendo a utilizar siempre el "-x" demasiado para no perder tiempo negociando X en un túnel)

31

En el constructo

something | 
while read x; do 
    ssh ... 
done 

la entrada estándar vista por el ciclo while es la salida de something.

El comportamiento predeterminado de ssh es leer la entrada estándar. Esto le permite hacer cosas como

cat id_rsa.pub | ssh new_box "cat - >> ~/.ssh/authorized_keys" 

Ahora, con eso se dice, cuando se lee el primer valor, el primer comando ssh leerá la entrada completa de something. Luego, cuando termina ssh, no hay salida, y se detiene read.

La solución es ssh -n ..., p.

cat /etc/hosts | awk '{print $2}' | while read x; do 
    ssh -n $x "do_something_on_the_machine" 
done 
+0

¡La mejor respuesta! – Jer

2

La mayoría de las respuestas son específicas de ssh. Otros comandos también secuestran stdin y no tienen una opción -n. Esto debería abordar cualquier otro comando. Esto también debería funcionar para ssh.

while read x; do 
    # Make sure command does not hijack stdin 
    echo "" | command $x 
done < /path/to/some/file