Los siguientes hilos en este sitio y StackOverflow fueron útiles para comprender cómo IFS
funciona:
- ¿En qué consiste IFS para el bucle?
- Cómo recorrer las líneas de un archivo
- Bash, leer línea por línea del archivo, con IFS
Pero todavía tengo algunas preguntas cortas. Decidí preguntarles en la misma publicación, ya que creo que puede ayudar a futuros lectores mejores:
Q1. IFS
se discute típicamente en el contexto de "división de campo". ¿La división de campos es lo mismo que la división de palabras ?
P2: La especificación POSIX dice :
Si el valor de IFS es nulo, no se realizará división de campo.
¿Es IFS=
lo mismo establecer IFS
que nulo? ¿Es esto lo que significa establecerlo también en un empty string
?
P3: En la especificación POSIX, leo lo siguiente:
Si no se establece IFS, el shell se comportará como si el valor de IFS fuera
<space>, <tab> and <newline>
Digamos que quiero restaurar el valor predeterminado de IFS
. ¿Cómo puedo hacer eso? (más específicamente, ¿cómo me refiero <tab>
y <newline>
?)
P4: Finalmente, ¿cómo este código:
while IFS= read -r line
do
echo $line
done < /path_to_text_file
comportarse si cambiamos la primera línea a
while read -r line # Use the default IFS value
o para:
while IFS=' ' read -r line
IFS
y un no establecidoIFS
son muy diferentes. La respuesta a Q4 es en parte incorrecta: los separadores internos no se tocan aquí, solo los iniciales y finales.IFS
, todas ellas significanIFS=
.IFS=' ' ; foo=( bar baz qux ) ; echo "${#foo[@]}"
. (Er, ¿qué? Debería haber múltiples delimitadores de espacio allí, SO motor sigue despojándolos).read
; la última variable toma todo lo que queda, excepto el último separador y deja separadores internos dentro.Q1: sí. "División de campo" y "división de palabras" son dos términos para el mismo concepto.
Q2: sí. Si no
IFS
está configurado (es decir, despuésunset IFS
), es equivalente aIFS
estar configurado en$' \t\n'
(un espacio, una pestaña y una nueva línea). SiIFS
se establece en un valor vacío (que es lo “nulo” significa aquí) (es decir, despuésIFS=
oIFS=''
oIFS=""
), sin la división de campos se realiza en absoluto (y$*
, lo que normalmente se utiliza el primer carácter$IFS
, utiliza un carácter de espacio).P3: Si desea tener el
IFS
comportamiento predeterminado , puede usarlounset IFS
. Si desea establecerIFS
explícitamente este valor predeterminado, puede poner los caracteres literales espacio, tabulación, nueva línea entre comillas simples. En ksh93, bash o zsh, puede usarIFS=$' \t\n'
. Portablemente, si desea evitar tener un carácter de tabulación literal en su archivo fuente, puede usarP4: Con el
IFS
conjunto en un valor vacío, seread -r line
estableceline
en toda la línea excepto su nueva línea de terminación. ConIFS=" "
, se recortan los espacios al principio y al final de la línea. Con el valor predeterminado deIFS
, las pestañas y los espacios se recortan.fuente
$@
, hay algunas variaciones entre shells en contextos no listados comoIFS=; var=$@
). Debe tenerse en cuenta que cuando IFS está vacío, no se realiza la división de palabras, pero $ var todavía se expande a ningún argumento en lugar de un argumento vacío cuando $ var está vacío, y el globbing todavía se aplica, por lo que aún necesita citar variables (incluso si desactivar globbing)Q1. División de campo.
Sí, ambos apuntan a la misma idea.
P2: ¿Cuándo es nulo IFS ?
Sí, los tres significan lo mismo: no se realizará división de campo / palabra. Además, esto afecta a los campos de impresión (como con
echo "$*"
) todos los campos se concatenarán juntos sin espacio.P3: (parte a) Desarmar IFS.
Que es exactamente equivalente a:
Eso significa que la 'División de campo' será exactamente la misma con un valor IFS predeterminado, o sin establecer.
Eso NO significa que IFS funcionará de la misma manera en todas las condiciones. Siendo más específico, la ejecución
OldIFS=$IFS
establecerá la varOldIFS
como nula , no la predeterminada. E intentar volver a establecer IFS, ya que estoIFS=OldIFS
establecerá IFS en nulo, no lo mantendrá sin configurar como antes. Cuidado !!.P3: (parte b) Restaurar IFS.
Para zsh, ksh y bash (AFAIK), IFS podría establecerse en el valor predeterminado como:
Hecho, no necesitas leer nada más.
Pero si necesita volver a configurar IFS para sh, puede volverse complejo.
Echemos un vistazo desde el más fácil de completar sin inconvenientes (excepto la complejidad).
1.- Desarmar IFS.
Podríamos simplemente
unset IFS
(Leer Q3 parte a, arriba).2.- Intercambiar caracteres.
Como solución alternativa, intercambiar el valor de tabulación y nueva línea simplifica la configuración del valor de IFS, y luego funciona de manera equivalente.
Establezca IFS en <space><newline> <tab> :
3.- ¿Un simple? solución:
Si hay secuencias de comandos secundarias que necesitan que IFS esté configurado correctamente, siempre puede escribir manualmente:
Donde la secuencia escrita manualmente fue:,
IFS=
'spacetabnewline'secuencia que realmente se ha escrito correctamente arriba (si necesita confirmar, edite esta respuesta). Pero una copia / pegar de su navegador se romperá porque el navegador exprimirá / ocultará el espacio en blanco. Hace que sea difícil compartir el código como se escribió anteriormente.4.- Solución completa.
Escribir código que se pueda copiar de forma segura generalmente implica escapes imprimibles inequívocos.
Necesitamos un código que "produzca" el valor esperado. Pero, incluso si es conceptualmente correcto, este código NO establecerá un final
\n
:Eso sucede porque, en la mayoría de los shells, todas las nuevas líneas finales
$(...)
o`...`
sustituciones de comandos se eliminan en la expansión.Necesitamos usar un truco para sh:
Una forma alternativa puede ser establecer IFS como un valor de entorno de bash (por ejemplo) y luego llamar a sh (las versiones del mismo que aceptan que IFS se establezca a través del entorno), de esta manera:
En resumen, sh hace que restablecer IFS a valores predeterminados sea una aventura bastante extraña.
P4: en el código real:
Primero: no sé si el
echo $line
(con la var NO citada) está ahí o no. Introduce un segundo nivel de 'división de campo' que la lectura no tiene. Entonces responderé a ambas. :)Con este código (para que puedas confirmar). Necesitará el útil xxd :
Yo obtengo:
El primer valor es solo el valor correcto de
IFS=
'spacetabnewline'La siguiente línea es todos los valores hexadecimales que tiene la var
$a
, y una nueva línea '0a' al final, ya que se le dará a cada comando de lectura.La siguiente línea, para la cual IFS es nulo, no realiza ninguna 'división de campo', pero la nueva línea se elimina (como se esperaba).
Las siguientes tres líneas, como IFS contiene un espacio, eliminan los espacios iniciales y configuran la línea var con el saldo restante.
Las últimas cuatro líneas muestran lo que hará una variable sin comillas. Los valores se dividirán en los (varios) espacios y se imprimirán como:
bar,baz,qux,
fuente
unset IFS
borra IFS, incluso si se supone que IFS es "\ t \ n":Probado en las versiones bash 4.2.45 y 3.2.25 con el mismo comportamiento.
fuente
unset
deIFS
, como se explica en los comentarios de la respuesta aceptada aquí.