Creo un archivo con campos delimitados por tabuladores.
echo foo$'\t'bar$'\t'baz$'\n'foo$'\t'bar$'\t'baz > input
Tengo el siguiente script llamado zsh.sh
#!/usr/bin/env zsh
while read line; do
<<<$line cut -f 2
done < "$1"
Lo pruebo
$ ./zsh.sh input
bar
bar
Esto funciona bien Sin embargo, cuando cambio la primera línea para invocar bash, falla.
$ ./bash.sh input
foo bar baz
foo bar baz
¿Por qué falla bashy funciona con esto zsh?
Solución de problemas adicional
- Usar rutas directas en el shebang en lugar de
envproducir el mismo comportamiento. - Tubería en
echolugar de usar la cadena aquí<<<$linetambién produce el mismo comportamiento. es decirecho $line | cut -f 2. - Utilizando en
awklugar decutobras para ambos proyectiles. es decir<<<$line awk '{print $2}'.
bash
zsh
quoting
whitespace
here-string
Gavilán
fuente
fuente

echo -e 'foo\tbar\tbaz\n...',echo $'foo\tbar\tbaz\n...'oprintf 'foo\tbar\tbaz\n...\n'o variaciones de éstos. Le ahorra tener que ajustar individualmente cada pestaña o nueva línea.Respuestas:
Lo que sucede es que
bashreemplaza las pestañas con espacios. Puede evitar este problema diciendo en su"$line"lugar, o cortando explícitamente espacios.fuente
\ty lo reemplaza con un espacio?<<< $line,bashse divide pero no es global. No hay razón para que se divida aquí, ya que<<<espera una sola palabra. Se divide y luego se une en ese caso, lo que tiene poco sentido y está en contra de todas las otras implementaciones de shells que han sido compatibles<<<antes o despuésbash. OMI es un error.Esto se debe a que
<<< $line, en , labashdivisión de palabras (aunque no engloba)$lineya que no se cita allí y luego une las palabras resultantes con el carácter de espacio (y lo coloca en un archivo temporal seguido de un carácter de nueva línea y lo convierte en el stdin decut).tabpasa a estar en el valor predeterminado de$IFS:La solución con
bashes citar la variable.Tenga en cuenta que es el único shell que hace eso.
zsh(de dónde<<<viene, inspirado en el puerto Unix derc)ksh93,mkshyyashque también es compatible<<<, no lo hagas.Cuando se trata de matrices,
mksh,yashyzshunirse en el primer carácter de$IFS,bashyksh93en el espacio.Hay una diferencia entre
zsh/yashymksh(versión R52 al menos) cuando$IFSestá vacío:El comportamiento es más consistente en todos los shells cuando se usa
"${a[*]}"(excepto quemkshtodavía tiene un error cuando$IFSestá vacío).En
echo $line | ..., ese es el operador habitual de split + glob en todos los shells similares a Bourne perozsh(y los problemas habituales asociados conecho).fuente
El problema es que no estás citando
$line. Para investigar, cambie los dos scripts para que simplemente impriman$line:y
Ahora, compare su salida:
Como puede ver, porque no está citando
$line, bash no interpreta las pestañas correctamente. Zsh parece lidiar con eso mejor. Ahora, secutusa\tcomo delimitador de campo por defecto. Por lo tanto, dado que subashscript está comiendo las pestañas (debido al operador split + glob),cutsolo ve un campo y actúa en consecuencia. Lo que realmente está ejecutando es:Entonces, para que su script funcione como se espera en ambos shells, cite su variable:
Entonces, ambos producen la misma salida:
fuente
bash.shComo ya se ha respondido, una forma más portátil de usar una variable es citarla:
Hay una diferencia de implementación en bash, con la línea:
Este es el resultado de la mayoría de los proyectiles:
Solo bash divide la variable a la derecha de
<<<cuando no está entre comillas.Sin embargo, eso se ha corregido en la versión 4.4 de bash.
Eso significa que el valor de
$IFSafecta el resultado de<<<.Con la linea:
Todos los shells usan el primer carácter de IFS para unir valores.
Con
"${l[@]}", se necesita un espacio para separar los diferentes argumentos, pero algunos shells eligen usar el valor de IFS (¿Es correcto?).Con un IFS nulo, los valores deben unirse, como con esta línea:
Pero tanto lksh como mksh no lo hacen.
Si cambiamos a una lista de argumentos:
Tanto yash como zsh no pueden mantener los argumentos separados. ¿Eso es un error?
fuente
zsh/yashy"${l[@]}"en contextos que no son listas, eso es por diseño, donde"${l[@]}"solo es especial en contextos de listas. En contextos que no son listas, no hay separación posible, debe unir los elementos de alguna manera. Unirse con el primer personaje de $ IFS es más consistente que unirse con un personaje espacial IMO.dashlo hace también (dash -c 'IFS=; a=$@; echo "$a"' x a b). Sin embargo, POSIX tiene la intención de cambiar ese IIRC. Ver esta (larga) discusiónvar=$@especificar.