Itens separados por IFS em loop

0

Recomenda-se usar o IFS para percorrer a lista separada por vírgulas, por exemplo, aqui . Mas vejo que

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. WASA?

    
por Val 11.07.2014 / 11:54

2 respostas

1

O principal problema aqui é que a segunda linha, IFS=, sentences1=($sentences) , define o IFS como "," permanentemente; Isso significa que você obtém um comportamento de análise estranho e inesperado em todo o restante do script. Observe que, com algo como IFS=, read ... , isso não aconteceria porque a atribuição do IFS é tratada como aplicável somente ao comando read ; mas em IFS=, sentences1=($sentences) , não há comando real, apenas atribuições, então as atribuições são tratadas como aplicáveis 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 é uma matriz (apenas uma string simples), portanto, o [@] bit não faz nada, mas como o IFS ainda é ",", ele é "dividido em palavras" em "Hello World" e " Perguntas "- ou seja, você obtém o resultado certo, mas pelo motivo errado.

(Nota: se fosse um array real, você gostaria de ter aspas duplas em torno dele, como em for sentence in "${sentences2[@]}" para evitar que os elementos sejam divididos em palavras.)

O próximo comando, for i in $(seq 1 2) , é onde o problema real começa. seq 1 2 imprime "1 [newline] 2 [newline]", a construção $( ) recorta a nova linha à direita e, em seguida, "1 [newline] 2" gera divisão de palavras, mas como não há vírgula, ela é tratada como uma palavra (que contém uma nova linha). Assim, o loop interno é executado apenas uma vez, com i definido como "1 [newline] 2".

A próxima linha, echo $i $sentence , imprime "1 [newline] 2" seguida por um espaço, seguido pelo conteúdo de sentence . Na primeira iteração, com sentence definido como "Hello World", isso imprime:

1 
2 Hello World

... a nova linha no meio faz parecer 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. Segundo, sempre duplique as referências de variáveis para evitar a divisão inesperada de palavras (e a expansão de curingas). Aqui está o que recebo para o 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
    
por 13.07.2014 / 19:12
0

Eu 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.

    
por 11.07.2014 / 12:20

Tags