
É recomendado usar IFS para percorrer listas separadas por vírgulas, por exemploaqui. Mas, eu vejo isso
sentences="Hello World,Questions"
IFS=, sentences1=($sentences) # if we comment this line then loop is fixed
sentences2=$sentences
for sentence in ${sentences2[@]};
do
for i in $(seq 1 2);
do
#This is fine - prints a new word every line
#echo $sentence
#This is fine - prints new number every line
#echo $i
echo $i $sentence
done
done
produz
1
2 Hello World
1
2 Question
Em vez de
1 Hello World
2 Hello World
1 Question
2 Question
desejado. ERA UM?
Responder1
O principal problema aqui é que a segunda linha, IFS=, sentences1=($sentences)
, define IFS como "," permanentemente; isso significa que você terá um comportamento de análise estranho e inesperado durante o restante do script. Observe que com algo como IFS=, read ...
isso não aconteceria porque a atribuição IFS é tratada como aplicada apenas ao read
comando; mas em IFS=, sentences1=($sentences)
, não há comando real, apenas atribuições, portanto as atribuições são tratadas como aplicadas a todo o shell.
Então, vamos dar uma olhada no que acontece no restante do script. sentences2=$sentences
apenas copia sentences
para sentences2
, então for sentence in ${sentences2[@]}
é um pouco estranho. sentences2
não é um array (apenas uma string simples), então o [@]
bit não faz nada, mas como IFS ainda é ",", ele é "dividido por palavras" em "Hello World" e "Questions" - ou seja, você obter o resultado certo, mas pelo motivo errado.
(Observação: se fosse uma matriz real, você gostaria de colocá-la entre aspas duplas, para for sentence in "${sentences2[@]}"
evitar que os elementos fossem divididos por palavras.)
O próximo comando, for i in $(seq 1 2)
, é onde começa o verdadeiro problema. seq 1 2
imprime "1[nova linha]2[nova linha]", a $( )
construção corta a nova linha final e então "1[nova linha]2" é dividida em palavras - mas como não há vírgula ali, ela é tratada como uma palavra (isso acontece com contém uma nova linha). Assim, o loop interno é executado apenas uma vez, i
definido como "1[nova linha]2".
A próxima linha, echo $i $sentence
, imprime "1[nova linha]2" seguido por um espaço, seguido pelo conteúdo de sentence
. Na primeira iteração, sentence
definida como "Hello World", isso imprime:
1
2 Hello World
... a nova linha no meio faz com que pareçam duas coisas sendo impressas, mas na verdade é apenas uma echo
que contém uma nova linha.
Portanto, duas grandes recomendações: primeiro, quando você alterar o IFS, altere-o novamente depois. Em segundo lugar, sempre coloque aspas duplas nas referências de variáveis para evitar divisão inesperada de palavras (e expansão de curingas). Aqui está o que recebo pelo script:
#!/bin/bash
sentences="Hello World,Questions"
saveIFS="$IFS"
IFS=, sentences1=($sentences)
IFS="$saveIFS" # Set IFS back to normal!
for sentence in "${sentences1[@]}"; do # Note double-quotes, and sentences1 is
an actual array
for i in $(seq 1 2); do # No double-quotes, we *want* seq's output to be spli
t
echo "$i" "$sentence" # Double-quotes for safety
done
done
Responder2
Não sei qual é o motivo, mas irc://freenode/bash fornece a solução
while IFS=, read -ra items; do
for item in "${items[@]}"; do
for i in {1..3}; do
printf '%d %s\n' "$i" "$item"
done
done
done <<< "Hello World,Questions,Answers"
que imprime corretamente.