For 루프 라인 - 하나의 "for" 문에만 IFS를 설정하는 방법은 무엇입니까?

For 루프 라인 - 하나의 "for" 문에만 IFS를 설정하는 방법은 무엇입니까?

다음은 내가 달성하려는 동작의 예입니다.

행 목록이 있고 각 행에는 공백으로 구분된 값이 포함되어 있다고 가정해 보겠습니다.

lines='John Smith
James Johnson'

사용자가 묻는 프롬프트를 누를 때 이름이나 성을 루프백하고 싶기 때문에 IFS를 전체적으로 변경합니다.

oIFS=$IFS
IFS='
'
for line in $lines; do
    IFS=$oIFS
    read name surname <<< $line
    read -p "Do you want to echo name? surname otherwise "
    if [[ $REPLY == "y" ]]; then
        echo $name
    else
        echo $surname
    fi
done

이것은 효과가 있지만 이 접근 방식은 나에게 적합하지 않은 것 같습니다.

  1. IFS를 변경했지만 아마도 복원하는 것을 잊었을 것입니다.
  2. 루프가 반복될 때마다 IFS를 복원합니다.

while IFS=...여기서는 다음과 같이 사용할 수 있다는 것을 알았습니다 .

while IFS='
' read line ; do
    echo $line
    read name surname <<< $line
    read -p "Do you want to echo name? surname otherwise "
    if [[ $REPLY == "y" ]]; then
        echo $name
    else
        echo $surname
    fi
done <<< "$lines"

read -p그러나 지속적인 입력 스트림으로 인해 프롬프트가 손상되므로 이는 옵션이 아닙니다.

한 가지 해결책은 for다음과 같이 하나의 명령문에만 IFS를 설정하는 것입니다.

IFS='
' for line in $lines; do
    ...
done

하지만 bash는 이것을 허용하지 않습니다.

답변1

mapfile먼저 /를 사용하여 입력 라인을 배열로 읽을 수 있습니다 readarray.

lines='John Smith
James Johnson'
mapfile -t lines <<< "$lines"
for line in "${lines[@]}"; do
    read name surname <<< "$line"
    echo "name: $name surname: $surname"
done

lines출력이 명령에서 나온 경우에도 마찬가지로 mapfile -t lines < <(somecommand)직접 사용할 수 있습니다. 프로세스 교체는 파이프와 약간 비슷하지만 서브쉘에서 실행되는 파이프 부분의 문제를 방지합니다. lines마지막 줄 끝에 개행 문자가 누락되었지만 하나 <<<를 추가했습니다. mapfile누락되더라도 신경 쓰지 마세요. 하지만 끝에 개행 문자가 있으면 lines추가 항목을 보관할 빈 배열 항목을 얻게 됩니다. 이 문제는 프로세스 교체를 사용하여 우회할 수 있습니다.


여기,

while IFS=... read line ; do
    ...
    read -p "Do you want to echo name? surname otherwise "
done <<< "$lines"

둘 다 read동일한 입력에서 읽지만 (테스트되지 않음) 다른 파일 설명자를 사용하여 루프로 리디렉션하여 이 문제를 해결할 수 있다고 생각합니다. 예:

while IFS=... read -u 3 line ; do
    ...
    read -p "Do you want to echo name? surname otherwise "
done 3<<< "$lines"

아니면 그게 중요한지 모르겠습니다 read <&3.read -u

답변2

조작하는 대신 실제 목록(배열)을 사용하십시오 IFS.


#!/bin/bash
lines=( 'John Smith' 'James Johnson' )

for line in "${lines[@]}"; do
  names=($line)
  echo "$line"
  read -p "Do you want to echo name? surname otherwise " 
    if [[ $REPLY == "y" ]]; then
        echo "${names[0]}"
    else
        echo "${names[1]}"
    fi
done

물론 이는 이름과 성만 있다고 가정합니다. 이는 실제 사람에게는 해당되지 않지만 데이터에는 해당될 수 있습니다. 어쨌든 원본 코드는 동일한 가정을 하고 있으므로 문제가 되지 않을 것 같습니다.

서브셸에서 루프를 실행하여 원하는 대로 이 작업을 수행할 수도 있으므로 변경 사항은 IFS서브셸에만 영향을 미칩니다.

#!/bin/bash
lines='John Smith
James Johnson'


(
  oIFS=$IFS
  IFS=$'\n'
  for line in $lines; do
    echo "$line"
    IFS=$oIFS
    read name surname <<< "$line"
    read -p "Do you want to echo name? surname otherwise "
    if [[ $REPLY == "y" ]]; then
        echo "$name"
    else
        echo "$surname"
    fi
  done
)

### The IFS hasn't been changed here outside the subshell.

관련 정보