bash cambia su comportamiento dependiendo del valor de la variable "IFS"

18

Cuando configuro la IFSvariable en un espacio, bashtrata los espacios múltiples como un espacio ( myprogrames un programa que imprime los argumentos de la línea de comandos que recibe):

IFS=" "
x="hello   hi   world"
./myprogram $x
argv[1] = hello
argv[2] = hi
argv[3] = world

Pero cuando configuro la IFSvariable como una coma, bashno trata las comas múltiples como una coma:

IFS=","
x="hello,,,hi,,,world"
./myprogram $x
argv[1] = hello
argv[2] = 
argv[3] = 
argv[4] = hi
argv[5] = 
argv[6] = 
argv[7] = world

¿Porqué es eso?

usuario267935
fuente
Solo como referencia, "IFS" significa separador de campo interno .
pr1268

Respuestas:

21

Esto está documentado en man bash. Una sola aparición de cualquier carácter en IFS que no sea un espacio en blanco delimita un campo.

De man bash:

El shell trata cada carácter de IFS como un delimitador, y divide los resultados de las otras expansiones en palabras usando estos caracteres como terminadores de campo. Si IFS no está establecido, o su valor es exactamente <space><tab><newline>, el valor predeterminado, entonces las secuencias de <space>, <tab>y <newline>al principio y al final de los resultados de las expansiones anteriores se ignoran, y cualquier secuencia de caracteres IFS que no esté al principio o al final sirve para delimitar palabras. Si IFS tiene un valor diferente al predeterminado, las secuencias de los espacios en blanco, tabulación y nueva línea se ignoran al principio y al final de la palabra, siempre que el carácter de espacio en blanco esté en el valor de IFS (un carácter de espacio en blanco IFS ) Cualquier carácter en IFS que no sea espacio en blanco IFS, junto con cualquier carácter de espacio en blanco IFS adyacente, delimita un campo. Una secuencia de caracteres de espacio en blanco IFS también se trata como un delimitador. Si el valor de IFS es nulo, no se divide la palabra. [Énfasis añadido.]

Ejemplos: división de campo

Si IFS no tiene caracteres de espacio en blanco, entonces el espacio en blanco se incluye en los campos:

$ ( IFS=',' x='one , two,three'; printf "<%s>\n" $x )
<one >
< two>
<three>

Si IFS tiene espacios en blanco y una coma, entonces las secuencias de espacios en blanco, seguidas de una coma, seguidas de secuencias de espacios en blanco, se tratan como un delimitador único:

$ ( IFS=' ,' x='one , two,three'; printf "<%s>\n" $x )
<one>
<two>
<three>

Las secuencias de comas se interpretan como secuencias de campos vacíos:

$ ( IFS=' ,' x='one,,,two,three'; printf "<%s>\n" $x )
<one>
<>
<>
<two>
<three>

Ejemplos: espacios en blanco iniciales y finales

Si IFS no contiene espacios en blanco, los espacios en blanco iniciales y finales se mantienen en los campos:

$ ( IFS=',' x='  one , two,three  ,'; printf "<%s>\n" $x )
<  one >
< two>
<three  >

Si IFS contiene espacios en blanco, se eliminan las secuencias de espacios en blanco iniciales o finales:

$ ( IFS=' ,' x='  one , two,three  ,'; printf "<%s>\n" $x )
<one>
<two>
<three>
John1024
fuente
quizás también valga la pena enfatizar "entonces las secuencias de los espacios en blanco, el espacio, la pestaña y la nueva línea se ignoran al principio y al final de la palabra, siempre que el espacio en blanco esté en el valor de IFS"
Jeff Schaller
@JeffSchaller Excelente idea: acabo de agregar una sección sobre eso.
John1024
¿Qué pasa si tiene un archivo separado por tabulaciones con algunos valores faltantes? es decir, no desea que las secuencias de pestañas se traten como una sola pestaña. Además, los campos contienen comas, así que no puedo usar eso como delimitador. ¿Es la única solución para usar algún otro delimitador (no pestañas)?
Davos
@Davos Para los datos con cada campo delimitado por una sola pestaña, puede ser más natural usar otras herramientas que manejen esto fácilmente, como awkcon la -F'\t'opción o cut. Alternativamente, si tiene una versión reciente de bash, puede analizar los campos usando readarrayla -d$'\t'opción.
John1024