Eu crio um arquivo com campos delimitados por tabulação.
echo foo$'\t'bar$'\t'baz$'\n'foo$'\t'bar$'\t'baz > input
Eu tenho o seguinte script chamado zsh.sh
#!/usr/bin/env zsh
while read line; do
<<<$line cut -f 2
done < "$1"
Eu testei.
$ ./zsh.sh input
bar
bar
Isso funciona bem. No entanto, quando altero a primeira linha para chamar bash
, ela falha.
$ ./bash.sh input
foo bar baz
foo bar baz
Por que isso falha bash
e funciona zsh
?
Solução de problemas adicionais
- Usar caminhos diretos no shebang em vez de
env
produzir o mesmo comportamento. - Tubulação com em
echo
vez de usar a cadeia aqui<<<$line
também produz o mesmo comportamento. ieecho $line | cut -f 2
. - Usando em
awk
vez decut
funciona para as duas conchas. ie<<<$line awk '{print $2}'
.
bash
zsh
quoting
whitespace
here-string
Sparhawk
fonte
fonte
echo -e 'foo\tbar\tbaz\n...'
,echo $'foo\tbar\tbaz\n...'
, ouprintf 'foo\tbar\tbaz\n...\n'
ou variações destes. Isso evita que você precise quebrar individualmente cada guia ou nova linha.Respostas:
O que acontece é que
bash
substitui as guias por espaços. Você pode evitar esse problema dizendo em"$line"
vez disso ou cortando explicitamente os espaços.fonte
\t
e o substitui por um espaço?<<< $line
,bash
divide, mas não em glob. Não há razão para isso se dividir aqui, pois<<<
espera uma única palavra. Ele se divide e se une nesse caso, o que faz pouco sentido e é contra todas as outras implementações de shells suportadas<<<
antes ou depoisbash
. OMI é um bug.Isso porque
<<< $line
, em ,bash
a divisão de palavras (embora não exagere) é ativada,$line
pois não é citada lá e, em seguida, junta as palavras resultantes ao caractere de espaço (e coloca isso em um arquivo temporário seguido por um caractere de nova linha e torna isso o stdin decut
).tab
passa a ter o valor padrão de$IFS
:A solução com
bash
é citar a variável.Observe que é o único shell que faz isso.
zsh
(de onde<<<
vem, inspirado na porta Unix derc
)ksh93
,mksh
eyash
que também suporta<<<
não o fazem.Quando se trata de matrizes,
mksh
,yash
ezsh
juntar-se no primeiro caractere de$IFS
,bash
eksh93
no espaço.Há uma diferença entre
zsh
/yash
emksh
(versão R52 pelo menos) quando$IFS
está vazio:O comportamento é mais consistente entre as conchas quando você usa
"${a[*]}"
(exceto quemksh
ainda possui um erro quando$IFS
está vazio).Em
echo $line | ...
, esse é o operador split + glob usual em todas as conchas do tipo Bourne, maszsh
(e os problemas usuais associadosecho
).fonte
O problema é que você não está citando
$line
. Para investigar, altere os dois scripts para que eles simplesmente imprimam$line
:e
Agora, compare sua saída:
Como você pode ver, porque você não está citando
$line
, as guias não são interpretadas corretamente pelo bash. Zsh parece lidar melhor com isso. Agora,cut
usa\t
como o delimitador de campo por padrão. Portanto, como seubash
script está consumindo as guias (por causa do operador split + glob),cut
apenas vê um campo e age de acordo. O que você realmente está executando é:Portanto, para que seu script funcione conforme o esperado nos dois shells, cite sua variável:
Então, ambos produzem a mesma saída:
fonte
bash.sh
Como já foi respondido, uma maneira mais portátil de usar uma variável é citá-la:
Há uma diferença de implementação no bash, com a linha:
Este é o resultado da maioria das conchas:
Somente o bash divide a variável à direita
<<<
quando não está entre aspas.No entanto, isso foi corrigido na versão 4.4 do bash.
Isso significa que o valor de
$IFS
afeta o resultado de<<<
.Com a linha:
Todos os shells usam o primeiro caractere do IFS para unir valores.
Com
"${l[@]}"
, é necessário um espaço para separar os diferentes argumentos, mas algumas shells optam por usar o valor do IFS (isso está correto?).Com um IFS nulo, os valores devem se unir, como nesta linha:
Mas tanto o lksh quanto o mksh não conseguem fazê-lo.
Se mudarmos para uma lista de argumentos:
Yash e zsh falham em manter os argumentos separados. Isso é um bug?
fonte
zsh
/yash
e"${l[@]}"
em um contexto que não é de lista, é por design que"${l[@]}"
é especial apenas em contextos de lista. Em contextos que não são de lista, não há separação possível, você precisa juntar os elementos de alguma forma. Unir-se ao primeiro caractere de $ IFS é mais consistente do que unir-se a um caractere de espaço IMO.dash
faz isso também (dash -c 'IFS=; a=$@; echo "$a"' x a b
). O POSIX, no entanto, pretende mudar esse IIRC. Veja esta discussão (longa)var=$@
não especificado.