La trampa es que
IFS=; while read..
establece IFS
para todo el entorno de shell fuera del bucle, mientras que
while IFS= read
lo redefine solo para la read
invocación (excepto en el shell Bourne). Puedes comprobar que haciendo un ciclo como
while IFS= read xxx; ... done
luego, después de tal bucle, echo "blabalbla $IFS ooooooo"
imprime
blabalbla
ooooooo
mientras que después
IFS=; read xxx; ... done
las IFS
estancias redefinidas: ahora echo "blabalbla $IFS ooooooo"
imprime
blabalbla ooooooo
Así que si se utiliza la segunda forma, usted tiene que recordar para restablecer: IFS=$' \t\n'
.
La segunda parte de esta pregunta se ha fusionado aquí , por lo que he eliminado la respuesta relacionada de aquí.
while IFS=X read
no se separaX
, perowhile IFS=X; read
sí ...while
no tiene mucho sentido - la condición dewhile
extremos en ese punto y coma, así que no hay lazo real ...read
se convierte en sólo el primer comando dentro del bucle de un solo elemento o no ... ? ¿Qué pasa con eldo
entonces ..?while
condición (antesdo
).IFS=
el trabajo, peroIFS=X
no lo hace ... (o tal vez he sobredosis en esto durante un tiempo .. pausa para el café necesita :)Veamos un ejemplo, con un texto de entrada cuidadosamente elaborado:
Son dos líneas, la primera comienza con un espacio y termina con una barra diagonal inversa. Primero, echemos un vistazo a lo que sucede sin precauciones
read
(pero utilizandoprintf '%s\n' "$text"
para imprimir cuidadosamente$text
sin ningún riesgo de expansión). (A continuación, se$
encuentra el indicador de comandos de la shell).read
comió las barras diagonales inversas: la barra diagonal inversa-nueva línea hace que la nueva línea se ignore, y la barra diagonal inversa-cualquier cosa ignora esa primera barra diagonal inversa. Para evitar el tratamiento especial de las barras invertidas, utilizamosread -r
.Eso es mejor, tenemos dos líneas como se esperaba. Las dos líneas casi contienen el contenido deseado: el doble espacio entre
hello
yworld
se ha retenido, porque está dentro de laline
variable. Por otro lado, el espacio inicial fue devorado. Esto se debe a queread
lee tantas palabras como le pasa variables, excepto que la última variable contiene el resto de la línea, pero aún comienza con la primera palabra, es decir, los espacios iniciales se descartan.Por lo tanto, para leer cada línea literalmente, debemos asegurarnos de que no haya división de palabras . Hacemos esto estableciendo la
IFS
variable en un valor vacío.Tenga en cuenta cómo establecemos
IFS
específicamente para la duración de laread
incorporada . LosIFS= read -r line
conjuntos de la variable de entornoIFS
(a un valor vacío) específicamente para la ejecución deread
. Esta es una instancia de la sintaxis de comando simple general : una secuencia (posiblemente vacía) de asignaciones de variables seguida de un nombre de comando y sus argumentos (también, puede lanzar redireccionamientos en cualquier punto). Comoread
es una función integrada, la variable nunca termina en el entorno de un proceso externo; no obstante, el valor de$IFS
es lo que estamos asignando allí mientras seread
esté ejecutando¹. Tenga en cuenta queread
no es una función integrada especial , por lo que la asignación solo dura su duración.Por lo tanto, nos encargamos de no cambiar el valor de
IFS
otras instrucciones que puedan depender de él. Este código funcionará sin importar en qué se haya configuradoIFS
inicialmente el código circundante , y no causará ningún problema si el código dentro del bucle se basaIFS
.Contraste con este fragmento de código, que busca archivos en una ruta separada por dos puntos. La lista de nombres de archivos se lee desde un archivo, un nombre de archivo por línea.
Si el bucle fuera
while IFS=; read -r name; do …
, entoncesfor dir in $PATH
no se dividiría$PATH
en componentes separados por dos puntos. Si el código fueraIFS=; while read …
, sería aún más obvio queIFS
no está configurado:
en el cuerpo del bucle.Por supuesto, sería posible restaurar el valor de
IFS
después de ejecutarread
. Pero eso requeriría conocer el valor anterior, que es un esfuerzo adicional.IFS= read
es el camino simple (y, convenientemente, también el camino más corto).¹ Y, si
read
se interrumpe por una señal atrapada, posiblemente mientras la trampa se está ejecutando, POSIX no lo especifica y depende del shell en la práctica.fuente
while IFS= read
(sin un punto y coma después=
) no es una forma especial dewhile
o deIFS
o deread
... La construcción es genérica: es decir.anyvar=anyvalue anycommand
. La falta de;
después del ajusteanyvar
hace que el alcance deanyvar
locales aanycommand
.. El tiempo - hacer bucle / hecho es 100% no relacionada con el ámbito localany_var
.Aparte de la (ya clarificada)
IFS
las diferencias de alcance entre elwhile IFS='' read
,IFS=''; while read
ywhile IFS=''; read
expresiones idiomáticas (por mando vs script / en toda la cáscaraIFS
ámbito de las variables), la lección para llevar a casa es que se pierde los líderes y espacios al final de una línea de entrada si la variable IFS se establece en (contiene un) espacio.Esto puede tener consecuencias bastante graves si se procesan las rutas de archivo.
Por lo tanto, establecer la variable IFS en la cadena vacía no es una mala idea, ya que garantiza que el espacio en blanco inicial y posterior de una línea no se elimine.
Ver también: Bash, leer línea por línea desde el archivo, con IFS
fuente
Inspirado por la respuesta de Yuzem
Si quieres establecer
IFS
un personaje real, esto funcionó para mífuente