El for
bucle está bien aquí. Pero tenga en cuenta que esto se debe a que el archivo contiene nombres de máquinas, que no contienen caracteres de espacio en blanco o caracteres globales. for x in $(cat file); do …
no funciona para iterar sobre las líneas file
en general, porque el shell primero divide la salida del comando en cat file
cualquier lugar donde haya espacios en blanco, y luego trata cada palabra como un patrón glob para \[?*
expandirse aún más. Puedes asegurarte for x in $(cat file)
si trabajas en ello:
set -f
IFS='
'
for x in $(cat file); do …
Lectura relacionada: ¿ Recorrer archivos con espacios en los nombres? ; ¿Cómo puedo leer línea por línea de una variable en bash? ; ¿Por qué se while IFS= read
usa con tanta frecuencia, en lugar de IFS=; while read..
? Tenga en cuenta que cuando se usa while read
, la sintaxis segura para leer líneas es while IFS= read -r line; do …
.
Ahora pasemos a lo que sale mal con su while read
intento. La redirección del archivo de lista del servidor se aplica a todo el bucle. Entonces, cuando se ssh
ejecuta, su entrada estándar proviene de ese archivo. El cliente ssh no puede saber cuándo la aplicación remota puede querer leer desde su entrada estándar. Entonces, tan pronto como el cliente ssh nota alguna entrada, envía esa entrada al lado remoto. El servidor ssh allí está listo para alimentar esa entrada al comando remoto, si lo desea. En su caso, el comando remoto nunca lee ninguna entrada, por lo que los datos terminan descartados, pero el lado del cliente no sabe nada al respecto. Su intento con echo
funcionó porque echo
nunca lee ninguna entrada, deja solo su entrada estándar.
Hay algunas maneras de evitar esto. Puede decirle a ssh que no lea desde la entrada estándar, con la -n
opción.
while read server; do
ssh -n $server "uname -a"
done < /home/kenny/list_of_servers.txt
De -n
hecho, la opción le dice ssh
que redirija su entrada desde /dev/null
. Puede hacerlo a nivel de shell y funcionará para cualquier comando.
while read server; do
ssh $server "uname -a" </dev/null
done < /home/kenny/list_of_servers.txt
Un método tentador para evitar la entrada de ssh procedente del archivo es poner la redirección en el read
comando: while read server </home/kenny/list_of_servers.txt; do …
. Esto no funcionará, porque hace que el archivo se abra nuevamente cada vez que read
se ejecuta el comando (por lo que leería la primera línea del archivo una y otra vez). La redirección debe estar en todo el ciclo while para que el archivo se abra una vez mientras dure el ciclo.
La solución general es proporcionar la entrada al bucle en un descriptor de archivo que no sea la entrada estándar. El shell tiene construcciones para transportar la entrada y la salida de un número de descriptor a otro. Aquí, abrimos el archivo en el descriptor de archivo 3 y redirigimos la read
entrada estándar del comando desde el descriptor de archivo 3. El cliente ssh ignora los descriptores abiertos no estándar, por lo que todo está bien.
while read server <&3; do
ssh $server "uname -a"
done 3</home/kenny/list_of_servers.txt
En bash, el read
comando tiene una opción específica para leer desde un descriptor de archivo diferente, para que pueda escribir read -u3 server
.
Lectura relacionada: descriptores de archivo y scripting de shell ; ¿Cuándo utilizarías un descriptor de archivo adicional?
Deberías usar
while
, nofor
. La forma de evitar que los comandos se traguen la entrada estándar en dicho bucle es simplemente usar otro descriptor de archivo :Para obtener más información,
help [r]ead
( realmente ) y otro artículo que explica por qué .fuente
-u
bandera? No estoy seguro de en qué página del manual debo buscarlo.help read
,help
es el comando para obtener el equivalente deman
páginas para shell construidos.En su primer código
ssh
"robará" el STDIN delwhile
. Añadir-n
opción assh
para evitarlo. Deman ssh
:fuente
for
bucle recibe los datos como parámetro, no como entrada.El manejo de entrada estándar predeterminado de
ssh
drena la línea restante del bucle while.Para evitar este problema, modifique de dónde lee el comando problemático la entrada estándar. Si no se necesita pasar una entrada estándar al comando, lea la entrada estándar del
/dev/null
dispositivo especial .fuente