Je crée un fichier avec des champs délimités par des tabulations.
echo foo$'\t'bar$'\t'baz$'\n'foo$'\t'bar$'\t'baz > input
J'ai le script suivant nommé zsh.sh
#!/usr/bin/env zsh
while read line; do
<<<$line cut -f 2
done < "$1"
Je le teste.
$ ./zsh.sh input
bar
bar
Cela fonctionne bien. Cependant, lorsque je modifie la première ligne à appeler à la bash
place, cela échoue.
$ ./bash.sh input
foo bar baz
foo bar baz
Pourquoi cela échoue bash
et fonctionne avec zsh
?
Dépannage supplémentaire
- L'utilisation de chemins directs dans le shebang au lieu de
env
produit le même comportement. - Tuyauter avec
echo
au lieu d'utiliser la chaîne here-string<<<$line
produit également le même comportement. ieecho $line | cut -f 2
. - Utilisation
awk
au lieu decut
travaux pour les deux coques. ie<<<$line awk '{print $2}'
.
bash
zsh
quoting
whitespace
here-string
Sparhawk
la source
la source
echo -e 'foo\tbar\tbaz\n...'
,echo $'foo\tbar\tbaz\n...'
ouprintf 'foo\tbar\tbaz\n...\n'
ou des variations de ceux - ci. Cela vous évite d'avoir à envelopper individuellement chaque onglet ou nouvelle ligne.Réponses:
Ce qui se passe c'est que
bash
les tabulations sont remplacées par des espaces. Vous pouvez éviter ce problème en disant à la"$line"
place ou en coupant explicitement les espaces.la source
\t
et le remplace par un espace?<<< $line
, sebash
divise mais pas glob. Il n'y a aucune raison qu'il se divise ici, comme le<<<
prévoit un seul mot. Il se divise puis rejoint dans ce cas, ce qui n'a pas de sens et est contre toutes les autres implémentations de shells qui ont pris en charge<<<
avant ou aprèsbash
. OMI c'est un bug.En effet, dans
<<< $line
, lebash
fractionnement des mots est activé (mais pas en globes)$line
car il n'y est pas cité, puis joint les mots résultants avec le caractère espace (et le place dans un fichier temporaire suivi d'un caractère de nouvelle ligne et en fait le stdin decut
).tab
se trouve être dans la valeur par défaut de$IFS
:La solution avec
bash
est de citer la variable.Notez que c'est le seul shell qui fait ça.
zsh
(d'où<<<
vient, inspiré par le port Unix derc
)ksh93
,mksh
etyash
qui supporte aussi<<<
ne le faites pas.En ce qui concerne les tableaux,
mksh
,yash
etzsh
rejoindre le premier caractère$IFS
,bash
etksh93
sur l' espace.Il y a une différence entre
zsh
/yash
etmksh
(version R52 au moins) quand$IFS
est vide:Le comportement est plus cohérent entre les shells lorsque vous utilisez
"${a[*]}"
(sauf qu'ilmksh
y a toujours un bug quand$IFS
est vide).Dans
echo $line | ...
, c'est l'opérateur split + glob habituel dans tous les shells de type Bourne maiszsh
(et les problèmes habituels associés àecho
).la source
Le problème est que vous ne citez pas
$line
. Pour enquêter, modifiez les deux scripts afin qu'ils s'impriment simplement$line
:et
Maintenant, comparez leur sortie:
Comme vous pouvez le voir, parce que vous ne citez pas
$line
, les onglets ne sont pas interprétés correctement par bash. Zsh semble mieux gérer cela. Maintenant,cut
utilise\t
comme délimiteur de champ par défaut. Par conséquent, puisque votrebash
script mange les onglets (en raison de l'opérateur split + glob),cut
ne voit qu'un seul champ et agit en conséquence. Ce que vous courez vraiment, c'est:Donc, pour que votre script fonctionne comme prévu dans les deux shells, citez votre variable:
Ensuite, les deux produisent la même sortie:
la source
bash.sh
Comme il a déjà été répondu, une façon plus portable d'utiliser une variable est de la citer:
Il y a une différence d'implémentation en bash, avec la ligne:
C'est le résultat de la plupart des obus:
Seul bash divise la variable à droite de
<<<
lorsqu'elle n'est pas citée.Cependant, cela a été corrigé dans la version 4.4 de bash.
Cela signifie que la valeur de
$IFS
affecte le résultat de<<<
.Avec la ligne:
Tous les shells utilisent le premier caractère d'IFS pour joindre des valeurs.
Avec
"${l[@]}"
, un espace est nécessaire pour séparer les différents arguments, mais certains shells choisissent d'utiliser la valeur d'IFS (est-ce correct?).Avec un IFS nul, les valeurs doivent devenir jointes, comme avec cette ligne:
Mais lksh et mksh n'y parviennent pas.
Si nous passons à une liste d'arguments:
Yash et zsh ne parviennent pas à séparer les arguments. Est-ce un bug?
la source
zsh
/yash
et"${l[@]}"
dans un contexte non-liste, c'est par conception où il"${l[@]}"
n'est spécial que dans des contextes de liste. Dans les contextes non listés, il n'y a pas de séparation possible, vous devez en quelque sorte joindre les éléments. La jonction avec le premier caractère de $ IFS est plus cohérente que la jonction avec un caractère espace IMO.dash
le fait aussi (dash -c 'IFS=; a=$@; echo "$a"' x a b
). Cependant, POSIX a l'intention de changer l'IIRC. Voir cette (longue) discussionvar=$@
non spécifié.