Q1. División de campo.
¿La división de campos es lo mismo que la división de palabras?
Sí, ambos apuntan a la misma idea.
P2: ¿Cuándo es nulo IFS ?
¿Establecer IFS=''
lo mismo como nulo, lo mismo que una cadena vacía también?
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.
En la especificación POSIX, leo lo siguiente :
Si IFS no está configurado, el shell se comportará como si el valor de IFS fuera <space><tab> <newline> .
Que es exactamente equivalente a:
Con un unset IFS
, el shell se comportará como si IFS es el predeterminado.
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 var OldIFS
como nula , no la predeterminada. E intentar volver a establecer IFS, ya que esto IFS=OldIFS
establecerá IFS en nulo, no lo mantendrá sin configurar como antes. Cuidado !!.
P3: (parte b) Restaurar IFS.
¿Cómo podría restaurar el valor de IFS al valor predeterminado? Digamos que quiero restaurar el valor predeterminado de IFS. ¿Cómo puedo hacer eso? (más específicamente, ¿cómo me refiero a <tab> y <newline> ?)
Para zsh, ksh y bash (AFAIK), IFS podría establecerse en el valor predeterminado como:
IFS=$' \t\n' # works with zsh, ksh, bash.
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> :
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3.- ¿Un simple? solución:
Si hay secuencias de comandos secundarias que necesitan que IFS esté configurado correctamente, siempre puede escribir manualmente:
IFS = '
'
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
:
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
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:
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
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:
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
En resumen, sh hace que restablecer IFS a valores predeterminados sea una aventura bastante extraña.
P4: en el código real:
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
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 :
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
Yo obtengo:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
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,
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.