Comportamiento de while read con bash

Imagen de mjllop
0 puntos

He observado que al usar while read con bash cambia el ámbito de la variable. Este fenómeno no se produce en otro tipo de bucles. Un ejemplo con peras y manzanas:

#!/bin/bash

#En este bucle no hay cambio
a="" #Inicialización de la variable
while [ ${#a} -lt 5 ]
do
     a=x$a
     echo "->$a" #Valor dentro del bucle
done
echo "=>$a" #Valor finalizado el bucle

#En este si hay cambio (se pierde el último valor de la variable)
echo "b" | while read r
do
     a=$a$r
     echo "->$a" #Valor dentro del bucle
done
echo "=>$a" #Valor finalizado el bucle

¿Alguna solución para mitigar la pérdida?

Gracias. 

 

Imagen de joseluis
+1
0
-1

Ya habia visto ese problema pero me pasaba lo mismo que a ti, que no conocia como evitarlo.
Rebuscando un poco he encontrado este hilo de esdebian donde dan una solucion que funciona (el problema de todo es la tuberia).

En concreto con tus peras y manzanas tendrias que hacer algo parecido a lo siguiente:

while read r
do
     a=$a$r
     echo "->$a" #Valor dentro del bucle
done <<< `echo "b"`
echo "=>$a" #Valor finalizado el bucle

Editado: No puse correctamente los menores del final.


Fdo. El forense asesino alias Censor fundamentalista.

+1
0
-1

Fdo. Forense asesino, Censor fundamentalista, Fustigador de novatos y Patético maleducado

Imagen de mjllop
+1
0
-1

No obstante, es válida para uno, .... Para dos no va muy fino (por lo que para N irá malamente, sobretodo si N son muchos cientos)

En este bucle se produce una única iteración y, además, aparece un espacio de forma espotánea. En estas condiciones, el bucle tiene una equivalencia:

a=xxx
while read -u0 r
do
   a=$a$r
   echo "->$r<->$a<-"
done <<< `echo "y";echo "z"`
echo "=>$a"

b=$(echo "y";echo "z")
echo "++>$a"

No me acaba de convencer mucho esta socución, no ...

De todos modos, muchas gracias por la referencia. Yo no había encontrado nada parecido.

Un saludo.

+1
0
-1
Imagen de joseluis
+1
0
-1

Tal vez poniendo un ejemplo un poco más real podamos encontrar alguna solución porque para poner echo "y";echo "x" pones directamente echo "xy" y ya no tienen ningún espacio entre medias. Es más, para ese ejemplo ni siquiera es necesario utilizar un while read.
Por supuesto doy por hecho que para lo que quieres hacer realmente necesitas el bucle y tampoco será factible el poner el echo "xy" porque seguro que es algo variable, pero como no me imagino el contexto en el que lo quieres usar no se me ocurre una solución sin abstraerme del ejemplo que has puesto :(

Se me acaba de ocurrir que también puedes poner en lugar de echo "y";echo "z" echo -n "y";echo -n "z", eso te elimina el molesto espacio, y el resultado final sería lo que buscas pero en cualquier caso sigue quedando un bucle de una sola iteración.

Bueno, lo dicho, tal vez con un ejemplo más real se nos ocurra algo.


Fdo. El forense asesino alias Censor fundamentalista.

+1
0
-1

Fdo. Forense asesino, Censor fundamentalista, Fustigador de novatos y Patético maleducado

Imagen de mjllop
+1
0
-1

Vaya por delante que para el tema que me llevo entre manos ya hemos encontrado una solución, gracias a las referencias que, gentilmente, nos has proporcionado.

Como podrás observar en el código que adjunto,  se trataba de componer la cadena correspondiente a la inversa de una dirección IPv6, apoyándonos en una función recursiva que nos trata cada uno de los bloques de 16 bits con los que se forman este tipo de direcciones. La idea es utilizarla como auxiliar para realizar una actualización remota de DNS con la utilidad nsupdate.

Bueno, contextualizado el problema ahí va el código:

#!/bin/bash
#---------------------------------------------------------
#--- Funcion recursiva para generar el formato inverso ---
#--- de una direccion IPv6 en bloques de 16 bits ---
#--- Argumentos de entrada: ---
#--- $1 - Bloques previos tratados ---
#--- $2 - Parte de la direccion no procesada ---
#--- Retorno de la funcion: ---
#--- Numero de bloques de 16 bits tratados ---
#---------------------------------------------------------
inv_ipv6()
{
typeset -i nbp=$1 #Numero de bloques previos al actual
typeset in=$2 #Buffer de entrada
typeset blq=${in%%:*} # Extrae bloque en tratamiento
typeset -r aux=0000 #Mascara auxiliar
typeset -i mxblq=8 #Numero maximo de bloques
typeset -i rtrn=0 #Retorno de la funcion para caso trivial
typeset blqs="" #Bloque formateado para salida por stdout
if [ ${#in} -ne 0 ]
then
# Hay información para procesar. Se elimina del buffer
# de entrada la que se tratará en la funcion y se envia
# a procesar el resto.
inv_ipv6 ${nbp}+1 ${in:(${#blq}+1)}
rtrn=$? #Numero de bloques procesados
# Tratamiento del bloque
if [ ${#blq} -eq 0 ]
then
# Se trata de bloques nulos omitidos (::)
let mxblq-=$nbp
let mxblq-=$rtrn
while [ $mxblq -gt 0 ]
do
blq=$blq$aux # Bloques con ceros
let rtrn+=1 # Incrementa tratados
let mxblq-=1 # Decrementa omitidos pendientes
done
else
# Se trata de un bloque de 16 bits
blq=${aux:${#blq}}${blq} # Completar con ceros por la izquierda
let rtrn+=1
fi

# Formatea la salida:
# - Invierte los digitos del bloque
# - Añade un punto a la derecha
while [ ${#blq} -gt 0 ]
do
blqs=$blqs${blq:${#blq}-1}.
blq=${blq%?}
done
# Salida del bloque formateado por por stdout de la funcion
echo $blqs
fi
return $rtrn #Numero de bloques de 16 bits tratados
}


ipv6="fd00:cafe:1f8::100"
echo "ipv6->$ipv6"


# Obtencion de la IPv6 inversa (1er. intento, no funciona)
#inv_ipv6 0 $ipv6 |\
#while read r
#do
#echo "==>$r<->$ipv6_i"
# ipv6_i=$ipv6_i$r
#done
#echo "ipv6_i->$ipv6_i"


# Obtencion de la IPv6 inversa (alternativa, si funciona)
ipv6_i=$(inv_ipv6 0 $ipv6|tr -d "[:cntrl:]")
ipv6_i=${ipv6_i%?} #Quita punto último por la derecha
echo "ipv6_i->$ipv6_i"

En este momento, el tema no es, exactamente, encontar una solución porque, como puedes ver, ya la hemos encontrado para el caso que nos ocupa. La cuestión es que esta solución es válida cuando el volumne de información que manejamos es pequeño. Imagina el sustituir los echos del ejemplo que ha iniciado esta conversación por un find / -print, por ejemplo.

Quizás exista alguna alternativa que no conozcamos para realizar este tipo de tratamientos. En caso contrario, y en mi opinión, el bash cuenta con una clara desventaja con respecto a otros intérpretes para el desarrollo, tal que ksh, por ejemplo.

Un saludo.

+1
0
-1
Imagen de joseluis
+1
0
-1

Por lo que he visto, en el ejemplo de los echos para conseguir lo que buscabas tenías que encerrar la ejecución de los echo's entre comillas dobles ". Es decir el script quedaría de la siguiente forma:

#!/bin/bash

a=xxx
while read r
do
   a=$a$r
   echo "->$r<->$a<-"
done <<< "`echo y;echo z`"
echo "=>$a"

b=$(echo "y";echo "z")
echo "++>$a"

De igual forma si en lugar de los echos tuvieras un find la línea final del bucle sería

done <<< "`find / -print`"

Fdo. El forense asesino alias Censor fundamentalista.

+1
0
-1

Fdo. Forense asesino, Censor fundamentalista, Fustigador de novatos y Patético maleducado

Imagen de joseluis
+1
0
-1

Movido desde Foro general.
Los problemas con scripts se suelen poner en "La línea de comandos".


Equipo de moderadores del foro
+1
0
-1

Fdo. Forense asesino, Censor fundamentalista, Fustigador de novatos y Patético maleducado