공백이있는 'for'루프에서 완전한 줄을 읽는 방법


128

for파일 루프 를 실행하려고하는데 전체 줄을 표시하고 싶습니다. 그러나 대신 마지막 단어 만 표시합니다. 나는 완전한 선을 원한다.

for j in `cat ./file_wget_med`

do
echo $j

done

실행 후 결과 :

Found.

내 데이터는 다음과 같습니다.

$ cat file_wget_med
2013-09-11 14:27:03 ERROR 404: Not Found.

답변:


178

for공백, 탭 또는 줄 바꿈과 같은 공백이 있으면 루프가 분할됩니다. 따라서 IFS (Internal Field Separator) 를 사용해야합니다 .

IFS=$'\n'       # make newlines the only separator
for j in $(cat ./file_wget_med)    
do
    echo "$j"
done
# Note: IFS needs to be reset to default!

5
IFS = $ "\ n"은 "nn"문자열도 분리하므로 IFS에 작은 따옴표를주의하십시오. 더 복잡한 구문이나 함수를 사용하는 것보다 IFS가 더 안정적인 것으로 나타났습니다.
erm3nda

23
unset IFS나중에 다른 명령이 동일한 쉘에서 영향을받지 않도록 나중에 권장됩니다 .
Boris Däppen

2
무슨 $IFS=$'\n'인가요?
Ren

2
$줄 바꿈이 문자열 내에서 작동하도록합니다. local IFS=$'\n'함수 내부 에서도 수행해야 합니다.
Andy Ray

1
@Renn $'...'" ANSI-C Quoting "
αғsнιη

82

for루프는 기본적으로 공백 (공백, 탭, 개행)에서 분리됩니다. 한 번에 한 줄씩 작업하는 가장 쉬운 방법 은 while read대신 바꿈 을 사용하여 루프 를 사용하는 것입니다.

while read i; do echo "$i"; done < ./file_wget_med

나는 당신의 명령이 한 줄에 한 단어를 뱉어 내기를 기대 합니다 (내 파일로 테스트 할 때 일어난 일입니다). 다른 일이 발생하면 그 원인이 무엇인지 잘 모르겠습니다.


7
또한 for i in `ls`; do echo $1; donewhile 명령으로 출력을 파이프하여 다음을 수행 할 수도 ls|while read i; do echo $i; done있습니다. 이것은 환경 변수를 변경할 필요없이 공백이있는 파일 / 폴더 이름에서 작동합니다. 아주 좋은 대답입니다.
jishi 2016 년

동안 IFS와 for 루프뿐만 아니라 첫 번째와 마지막 줄을 처리하지 않았다
grantbow

@jishi 주로 디스플레이 용으로 설계되었으며 터미널 창 크기에 반응하기 때문에 (파이프 연산자) ls와 함께 사용하기에 안전하지 않은 것으로 간주됩니다 |. find이 목적을 위해 더 안전하고 유연합니다.
SeldomNeedy

3
이것은 훌륭한 답변입니다 .Mac OSX에서 개발중인 IOS 앱의 목록을 PLIST 항목으로 바꾸는 데 사용했습니다. pbpaste- >pbpaste | while read t; do echo "<string>$t</string>"; done
Big Rich

3
ls -1함께 사용할 수 있습니다 |한 당 라인 포맷되지 않은 출력을 보장하기 위해
AndreyS Scherbakov

19
#!/bin/bash
files=`find <subdir> -name '*'`
while read -r fname; do
    echo $fname
done <<< "$files"

검증 된 작업, 원하는 라이너는 아니지만 우아하게 작업 할 수는 없습니다.


1
여기 끈! 다른 모든 솔루션 중에서 가장 우아함
Eliran Malka

5

다음은 Mitchell Currie의 답변에 대한 약간의 확장입니다. 부작용의 범위가 작기 때문에 좋아합니다. 변수를 설정할 필요가 없습니다.

#!/bin/bash
while read -r fname; do
    echo $fname
done <<< "`find <subdir>`"

1
당신은 제거 -name '*'할 수 있고 동일 할 것입니다.
wjandrea

1

나는 이것을 다음과 같이 쓸 것이다.

cat ./file_wget_med | while read -r j
do
    echo $j
done

원본 스크립트에서 최소한의 변경이 필요하기 때문에 (를 사용하는 솔루션은 제외 IFS하지만 bash이 루프 제어문의 동작 을 수정 하지는 않습니다).


1
파이프 cat는 불필요합니다. while루프는 리디렉션을 잘 받아들입니다. 이것이 보통 다음과 같이 쓰여집니다.while IFS= read -r line; do...done < input.txt
Sergiy Kolodyazhnyy

0

맵 파일은 로하지 휴대용 인덱스 배열로 파일에서 라인을 읽을 수있는 편리한 방법입니다 읽을 수 있지만 약간 빠른. for 루프를 사용하면 서브 쉘 작성을 피할 수 있습니다.

#!/bin/bash

mapfile -t < file.txt

for line in "${MAPFILE[@]}"; do
    echo $line
done

파이프 라인을 사용할 때 while 루프를 서브 쉘에 넣습니다. while 루프 형 변수 내부의 변경 사항은 스크립트의 외부 부분으로 전파되지 않습니다.

예:

#!/bin/bash

a=0
printf %s\\n {0..5} | while read; do
  ((a++))
done
echo $a # 'a' will always be 0.

(더 나은 해결책) :

#!/bin/bash

b=0
while read; do
  ((b++))
done < <(printf %s\\n {0..5})

echo $b # 'b' equal to 6 (works as expected).

-1

Dandalf는 함수형 솔루션에 가까워졌지만 알 수없는 양의 입력 결과 find ~/.gdfuse -name '*'를 변수 에 할당하려고 시도해서는 안됩니다 ! 또는 적어도 배열 변수를 통해 그러한 일을 시도하십시오. 당신이 너무 게으르다 고 주장한다면! 여기 Dandalf의 솔루션은 위험한 조작없이 수행됩니다. 그리고 한 줄로

while read -r fname; do
  echo $fname;
done <<< `find ~/.gdfuse -name '*'

안녕하세요 @odoncaoa, 큰 따옴표없이 find 명령을 시도했지만 모든 결과가 한 줄로 나타납니다. 이 명령과 관련된 "알 수없는 양의 입력"에 대해 혼동되고 있습니다. 위험의 예와 이론적으로 어떻게 일어날 수 있는가? 나는 그것에 대해 게으 르기를 원하지 않고, 다른 프로그래밍 언어에서 더 편안합니다.
Dandalf
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.