GNU/Linux >> Tutoriales Linux >  >> Linux

Lectura de líneas de un archivo con Bash:para vs. ¿Mientras?

Estoy tratando de leer un archivo de texto y hacer algo con cada línea, usando un script bash.

Entonces, tengo una lista que se ve así:

server1
server2
server3
server4

Pensé que podría repetir esto usando un ciclo while, así:

while read server; do
  ssh $server "uname -a"
done < /home/kenny/list_of_servers.txt

El ciclo while se detiene después de 1 ejecución, por lo que solo ejecuta uname -a en servidor1

Sin embargo, con un bucle for usando cat funciona bien:

for server in $(cat /home/kenny/list_of_servers.txt) ; do
  ssh $server "uname -a"
done

Aún más desconcertante para mí es que esto también funciona:

while read server; do
  echo $server
done < /home/kenny/list_of_servers.txt

¿Por qué mi primer ejemplo se detiene después de la primera iteración?

Respuesta aceptada:

El for el bucle está bien aquí. Pero tenga en cuenta que esto se debe a que el archivo contiene nombres de máquinas, que no contienen espacios en blanco ni caracteres globales. for x in $(cat file); do … no funciona para iterar sobre las líneas de file en general, porque el shell primero divide la salida del comando cat file en cualquier lugar donde haya espacios en blanco, y luego trata cada palabra como un patrón global, por lo que \[?* se amplían aún más. Puedes hacer for x in $(cat file) seguro si trabajas en ello:

set -f
IFS='
'
for x in $(cat file); do …

Lectura relacionada:¿Recorriendo archivos con espacios en los nombres?; ¿Cómo puedo leer línea por línea de una variable en bash?; ¿Por qué while IFS= read usado tan a menudo, en lugar de IFS=; while read.. ? Tenga en cuenta que al usar while read , la sintaxis segura para leer líneas es while IFS= read -r line; do … .

Ahora pasemos a lo que va mal con su while read intento. La redirección desde el archivo de la lista de servidores se aplica a todo el bucle. Así que cuando ssh se ejecuta, su entrada estándar proviene de ese archivo. El cliente ssh no puede saber cuándo la aplicación remota podría 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 enviar esa entrada al comando remoto, en caso de que lo desee. 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. Tu 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 -n opción.

while read server; do
  ssh -n $server "uname -a"
done < /home/kenny/list_of_servers.txt

El -n opción de hecho le dice a ssh para redirigir 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 que la entrada de ssh provenga del archivo es poner la redirección en read comando:while read server </home/kenny/list_of_servers.txt; do … . Esto no funcionará, porque hace que el archivo se vuelva a abrir 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 ser en todo el ciclo while para que el archivo se abra una vez durante la duración del ciclo.

Relacionado:¿BashScript funciona desde Terminal pero no desde CronTab?

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 entradas y salidas de un número de descriptor a otro. Aquí, abrimos el archivo en el descriptor de archivo 3 y redirigimos el read entrada estándar del comando del descriptor de archivo 3. El cliente ssh ignora los descriptores no estándar abiertos, 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 El comando tiene una opción específica para leer desde un descriptor de archivo diferente, por lo que puede escribir read -u3 server .

Lectura relacionada:descriptores de archivos y secuencias de comandos de shell; ¿Cuándo usaría un descriptor de archivo adicional?


Linux
  1. 10 prácticos alias de Bash para Linux

  2. ¿Archivo equivalente ".bashrc" leído por todos los shells?

  3. ¿Reemplazar líneas que coinciden con un patrón con líneas de otro archivo en orden?

  4. ¿Cómo llenar un archivo con una secuencia de /dev/urandom con un número específico de líneas?

  5. Lectura de archivos Rdata con codificación diferente

Cómo leer archivos línea por línea en Bash

Cómo leer un archivo línea por línea en Bash

Bash For Loop con ejemplos prácticos

Redirección de Bash explicada con ejemplos

Bash scripting:cómo leer datos de archivos de texto

Bash Scripting Part2 – Bucles for y while con ejemplos