루프 내 IFS로 구분된 항목

루프 내 IFS로 구분된 항목

쉼표로 구분된 목록을 반복하려면 IFS를 사용하는 것이 좋습니다. 예:여기. 그런데 그러고 보니

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

생산하다

1 
2 Hello World
1
2 Question

대신에

1 Hello World
2 Hello World
1 Question 
2 Question

원하는. 했다?

답변1

여기서 주요 문제는 두 번째 줄인 가 IFS=, sentences1=($sentences)IFS를 영구적으로 ","로 설정한다는 것입니다. 이는 스크립트의 나머지 부분 전체에서 이상하고 예상치 못한 구문 분석 동작이 발생한다는 것을 의미합니다. 와 같은 경우에는 IFS=, read ...IFS 할당이 명령에만 적용되는 것으로 처리되므로 이런 일이 발생하지 않습니다 read. 하지만 에는 IFS=, sentences1=($sentences)실제 명령이 없고 할당만 있으므로 할당은 전체 셸에 적용되는 것으로 처리됩니다.

이제 스크립트의 나머지 부분에서 무슨 일이 일어나는지 살펴보겠습니다. sentences2=$sentences그냥 sentences에 복사 sentences2하므로 for sentence in ${sentences2[@]}조금 이상합니다. sentences2배열(단지 일반 문자열)이 아니므로 비트는 [@]아무 작업도 수행하지 않지만 IFS는 여전히 ","이므로 "Hello World" 및 "Questions"로 "단어 분할"을 가져옵니다. 즉, 올바른 결과를 얻었지만 잘못된 이유가 있습니다.

for sentence in "${sentences2[@]}"(참고: 실제 배열인 경우 요소가 단어로 분할되는 것을 방지하기 위해 주위에 큰따옴표를 사용하는 것이 좋습니다 .)

다음 명령인 는 for i in $(seq 1 2)실제 문제가 시작되는 곳입니다. seq 1 2"1[newline]2[newline]"을 인쇄하고 $( )구문이 후행 줄 바꿈을 다듬은 다음 "1[newline]2"가 단어 분할됩니다. 하지만 거기에 쉼표가 없기 때문에 한 단어로 처리됩니다. 개행 문자를 포함합니다). 따라서 내부 루프는 i"1[newline]2"로 설정되어 한 번만 실행됩니다.

다음 줄 , echo $i $sentence은 "1[newline]2", 공백, 의 내용을 인쇄합니다 sentence. sentence"Hello World"로 설정된 첫 번째 반복에서는 다음이 인쇄됩니다.

1 
2 Hello World

...가운데에 줄 바꿈이 있으면 두 가지가 인쇄되는 것처럼 보이지만 실제로는 echo줄 바꿈이 포함된 항목일 뿐입니다.

따라서 두 가지 큰 권장 사항은 다음과 같습니다. 첫째, IFS를 변경할 때 나중에 다시 변경하십시오. 둘째, 예상치 못한 단어 분할(및 와일드카드 확장)을 방지하려면 항상 변수 참조를 큰따옴표로 묶으십시오. 스크립트로 얻은 내용은 다음과 같습니다.

#!/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

답변2

이유가 무엇인지 모르겠지만 irc://freenode/bash가 솔루션을 제공합니다.

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"

제대로 인쇄됩니다.

관련 정보