질문자 :Peter Mortensen
Bash 로 텍스트 파일의 각 줄을 어떻게 반복합니까?
이 스크립트로:
echo "Start!" for p in (peptides.txt) do echo "${p}" done
화면에 다음 출력이 표시됩니다.
Start! ./runPep.sh: line 3: syntax error near unexpected token `(' ./runPep.sh: line 3: `for p in (peptides.txt)'
(나중에 화면에 출력하는 것보다 $p
더 복잡한 일을 하고 싶습니다.)
환경 변수 SHELL 은 (env에서):
SHELL=/bin/bash
/bin/bash --version
출력:
GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu) Copyright (C) 2005 Free Software Foundation, Inc.
cat /proc/version
출력:
Linux version 2.6.18.2-34-default (geeko@buildhost) (gcc version 4.1.2 20061115 (prerelease) (SUSE Linux)) #1 SMP Mon Nov 27 11:46:27 UTC 2006
파일 peptides.txt에는 다음이 포함되어 있습니다.
RKEKNVQ IPKKLLQK QYFHQLEKMNVK IPKKLLQK GDLSTALEVAIDCYEK QYFHQLEKMNVKIPENIYR RKEKNVQ VLAKHGKLQDAIN ILGFMK LEDVALQILL
한 가지 방법은 다음과 같습니다.
while read p; do echo "$p" done <peptides.txt
주석에서 지적했듯이 이것은 선행 공백을 자르고 백슬래시 시퀀스를 해석하며 종료 줄 바꿈이 없는 경우 마지막 줄을 건너뛰는 부작용이 있습니다. 이러한 우려 사항이 있는 경우 다음을 수행할 수 있습니다.
while IFS="" read -rp || [ -n "$p" ] do printf '%s\n' "$p" done < peptides.txt
예외적으로 루프 본문이 표준 입력에서 읽을 수 있는 경우 다른 파일 설명자를 사용하여 파일을 열 수 있습니다.
while read -u 10 p; do ... done 10<peptides.txt
여기서 10은 임의의 숫자(0, 1, 2와 다름)입니다.
Bruno De Frainecat peptides.txt | while read line do # do something with $line here done
한 줄짜리 변형:
cat peptides.txt | while read line; do something_with_$line_here; done
이 옵션은 후행 줄 바꿈이 없는 경우 파일의 마지막 줄을 건너뜁니다.
다음을 통해 이를 방지할 수 있습니다.
cat peptides.txt | while read line || [[ -n $line ]]; do # do something with $line here done
Warren Young옵션 1a: While 루프: 한 번에 한 줄: 입력 리디렉션
#!/bin/bash filename='peptides.txt' echo Start while read p; do echo "$p" done < "$filename"
옵션 1b: While 루프: 한 번에 한 줄:
파일을 열고 파일 설명자(이 경우 파일 설명자 #4)에서 읽습니다.
#!/bin/bash filename='peptides.txt' exec 4<"$filename" echo Start while read -u4 p ; do echo "$p" done
Stan Graves이것은 다른 답변보다 낫지 않지만 공백 없이 파일에서 작업을 완료하는 또 다른 방법입니다(주석 참조). 나는 종종 별도의 스크립트 파일을 사용하는 추가 단계 없이 텍스트 파일의 목록을 파헤치기 위해 한 줄짜리가 필요하다는 것을 알게 되었습니다.
for word in $(cat peptides.txt); do echo $word; done
이 형식을 사용하면 모든 것을 하나의 명령줄에 넣을 수 있습니다. "echo $word" 부분을 원하는 대로 변경하고 세미콜론으로 구분된 여러 명령을 실행할 수 있습니다. 다음 예제에서는 파일의 내용을 사용자가 작성했을 수 있는 두 개의 다른 스크립트에 대한 인수로 사용합니다.
for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done
또는 이것을 스트림 편집기처럼 사용하려는 경우(sed 배우기) 다음과 같이 출력을 다른 파일에 덤프할 수 있습니다.
for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done > outfile.txt
한 줄에 한 단어로 만든 텍스트 파일을 사용했기 때문에 위에서 작성한 대로 사용했습니다. (주석 참조) 단어/줄을 분할하고 싶지 않은 공백이 있는 경우 조금 더 보기 흉해 지지만 동일한 명령은 여전히 다음과 같이 작동합니다.
OLDIFS=$IFS; IFS=$'\n'; for line in $(cat peptides.txt); do cmd_a.sh $line; cmd_b.py $line; done > outfile.txt; IFS=$OLDIFS
이것은 쉘이 공백이 아닌 개행으로만 분할하도록 지시한 다음 환경을 이전 상태로 되돌립니다. 이 시점에서 모든 것을 한 줄로 압축하는 것보다 쉘 스크립트에 모두 넣는 것을 고려할 수 있습니다.
행운을 빕니다!
mightypile다른 답변에서 다루지 않은 몇 가지 추가 사항:
구분된 파일에서 읽기
# ':' is the delimiter here, and there are three fields on each line in the file # IFS set below is restricted to the context of `read`, it doesn't affect any other code while IFS=: read -r field1 field2 field3; do # process the fields # if the line has less than three fields, the missing fields will be set to an empty string # if the line has more than three fields, `field3` will get all the values, including the third field plus the delimiter(s) done < input.txt
프로세스 대체를 사용하여 다른 명령의 출력에서 읽기
while read -r line; do # process the line done < <(command ...)
이 접근 방식은 command ... | while read -r line; do ...
왜냐하면 여기서 while 루프는 후자의 경우와 같이 서브쉘이 아닌 현재 쉘에서 실행되기 때문입니다. 관련 포스트 보기 while 루프 내에서 수정된 변수는 기억되지 않습니다 .
null로 구분된 입력에서 읽기(예: find ... -print0
while read -r -d '' line; do # logic # use a second 'read ... <<< "$line"' if we need to tokenize the line done < <(find /path/to/dir -print0)
관련 읽기: BashFAQ/020 - 줄 바꿈, 공백 또는 둘 다를 포함하는 파일 이름을 찾고 안전하게 처리하려면 어떻게 해야 합니까?
한 번에 둘 이상의 파일에서 읽기
while read -u 3 -r line1 && read -u 4 -r line2; do # process the lines # note that the loop will end when we reach EOF on either of the files, because of the `&&` done 3< input1.txt 4< input2.txt
@chepner의 답변을 기반으로 다음과 같습니다 .
-u
는 bash 확장입니다. POSIX 호환성을 위해 각 호출은 read -r X <&3
처럼 보일 것입니다.
전체 파일을 배열로 읽기(Bash 4 이전 버전)
while read -r line; do my_array+=("$line") done < my_file
파일이 불완전한 줄로 끝나는 경우(끝에 줄 바꿈이 없음) 다음을 수행합니다.
while read -r line || [[ $line ]]; do my_array+=("$line") done < my_file
전체 파일을 배열로 읽기(Bash 버전 4x 이상)
readarray -t my_array < my_file
또는
mapfile -t my_array < my_file
그리고
for line in "${my_array[@]}"; do # process the lines done
관련 게시물:
codeforester다음과 같이 while 루프를 사용합니다.
while IFS= read -r line; do echo "$line" done <file
노트:
IFS
제대로 설정하지 않으면 들여쓰기가 손실됩니다.
거의 항상 -r 옵션을 읽기와 함께 사용해야 합니다.
for
줄을 읽지 마십시오.
Jahid다음 파일이 있다고 가정합니다.
$ cat /tmp/test.txt Line 1 Line 2 has leading space Line 3 followed by blank line Line 5 (follows a blank line) and has trailing space Line 6 has no ending CR
많은 Bash 솔루션에서 읽는 파일 출력의 의미를 변경하는 네 가지 요소가 있습니다.
- 빈 줄 4;
- 두 줄의 선행 또는 후행 공백
- 개별 행의 의미 유지(즉, 각 행은 레코드임)
- 라인 6은 CR로 끝나지 않습니다.
빈 줄과 CR 없이 끝나는 줄을 포함하여 한 줄씩 텍스트 파일을 원하면 while 루프를 사용해야 하고 마지막 줄에 대한 대체 테스트가 있어야 합니다.
다음은 파일을 변경할 수 있는 방법입니다( cat
반환하는 것과 비교):
1) 마지막 줄과 선행 및 후행 공백을 잃습니다.
$ while read -rp; do printf "%s\n" "'$p'"; done </tmp/test.txt 'Line 1' 'Line 2 has leading space' 'Line 3 followed by blank line' '' 'Line 5 (follows a blank line) and has trailing space'
(만약 당신이 while IFS= read -rp; do printf "%s\n" "'$p'"; done </tmp/test.txt
대신한다면, 당신은 선행과 후행 공백을 보존하지만 다음과 같은 경우에 여전히 마지막 줄을 잃게 됩니다. CR로 종료되지 않음)
cat
프로세스 대체를 사용하면 전체 파일을 한 번에 읽고 개별 행의 의미를 잃게 됩니다.
$ for p in "$(cat /tmp/test.txt)"; do printf "%s\n" "'$p'"; done 'Line 1 Line 2 has leading space Line 3 followed by blank line Line 5 (follows a blank line) and has trailing space Line 6 has no ending CR'
$(cat /tmp/test.txt)
에서 "
를 제거하면 한 마디가 아닌 단어 단위로 파일을 읽게 됩니다. 또한 의도한 바가 아닐 수도 있습니다...)
파일을 한 줄씩 읽고 모든 간격을 유지하는 가장 강력하고 간단한 방법은 다음과 같습니다.
$ while IFS= read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt 'Line 1' ' Line 2 has leading space' 'Line 3 followed by blank line' '' 'Line 5 (follows a blank line) and has trailing space ' 'Line 6 has no ending CR'
선행 및 거래 공간을 제거하려면 IFS=
부분을 제거하십시오.
$ while read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt 'Line 1' 'Line 2 has leading space' 'Line 3 followed by blank line' '' 'Line 5 (follows a blank line) and has trailing space' 'Line 6 has no ending CR'
\n
없는 텍스트 파일은 상당히 일반적이지만 POSIX에서는 손상된 것으로 간주됩니다. 후행 \n
while
루프에서 || [[ -n $line ]]
필요하지 않습니다.)
BASH FAQ 에서 자세히 알아보기
dawg개행 문자로 읽기를 중단하지 않으려면 다음을 사용하십시오.
#!/bin/bash while IFS='' read -r line || [[ -n "$line" ]]; do echo "$line" done < "$1"
그런 다음 파일 이름을 매개 변수로 사용하여 스크립트를 실행합니다.
Anjul Sharma#!/bin/bash # # Change the file name from "test" to desired input file # (The comments in bash are prefixed with #'s) for x in $(cat test.txt) do echo $x done
Sinewhile
대신 xargs
를 사용하는 것을 좋아합니다. xargs
는 강력하고 명령줄 친화적입니다.
cat peptides.txt | xargs -I % sh -c "echo %"
xargs
-t
-p
하여 유효성 검사를 추가할 수도 있습니다.
hamou92다음은 다른 프로그램 출력의 행을 반복하고, 하위 문자열을 확인하고, 변수에서 큰따옴표를 삭제하고, 루프 외부에서 해당 변수를 사용하는 방법의 실제 예입니다. 꽤 많은 사람들이 조만간 이러한 질문을 할 것 같습니다.
##Parse FPS from first video stream, drop quotes from fps variable ## streams.stream.0.codec_type="video" ## streams.stream.0.r_frame_rate="24000/1001" ## streams.stream.0.avg_frame_rate="24000/1001" FPS=unknown while read -r line; do if [[ $FPS == "unknown" ]] && [[ $line == *".codec_type=\"video\""* ]]; then echo ParseFPS $line FPS=parse fi if [[ $FPS == "parse" ]] && [[ $line == *".r_frame_rate="* ]]; then echo ParseFPS $line FPS=${line##*=} FPS="${FPS%\"}" FPS="${FPS#\"}" fi done <<< "$(ffprobe -v quiet -print_format flat -show_format -show_streams -i "$input")" if [ "$FPS" == "unknown" ] || [ "$FPS" == "parse" ]; then echo ParseFPS Unknown frame rate fi echo Found $FPS
루프 외부에서 변수를 선언하고 값을 설정하고 루프 외부에서 사용하려면 done <<< "$(...)" 구문이 필요합니다. 애플리케이션은 현재 콘솔의 컨텍스트 내에서 실행되어야 합니다. 명령 주위의 따옴표는 출력 스트림의 줄 바꿈을 유지합니다.
하위 문자열에 대한 루프 일치는 이름=값 쌍을 읽고, 마지막 = 문자의 오른쪽 부분을 분할하고, 첫 번째 따옴표를 삭제하고, 마지막 따옴표를 삭제하고, 다른 곳에서 사용할 깨끗한 값을 갖습니다.
Whome이것은 가장 간단한 답변일 수 있고 모든 경우에 작동하지 않을 수도 있지만 저에게는 잘 작동합니다.
while read line;do echo "$line";done<peptides.txt
공백을 괄호로 묶어야 하는 경우:
while read line;do echo \"$line\";done<peptides.txt
아, 이것은 가장 많은 찬성을 얻은 답변과 거의 같지만 한 줄에 모두 있습니다.
xekon@Peter: 이것은 당신을 위해 일할 수 있습니다-
echo "Start!";for p in $(cat ./pep); do echo $p done
이것은 출력을 반환합니다-
Start! RKEKNVQ IPKKLLQK QYFHQLEKMNVK IPKKLLQK GDLSTALEVAIDCYEK QYFHQLEKMNVKIPENIYR RKEKNVQ VLAKHGKLQDAIN ILGFMK LEDVALQILL
Alan Jebakumar이것은 다소 매우 늦게 오고 있지만 누군가에게 도움이 될 수 있다고 생각하여 답변을 추가합니다. 또한 이것이 최선의 방법이 아닐 수도 있습니다. head
-n
인수와 함께 사용하여 파일의 시작 부분에서 n줄 을 읽을 tail
명령을 사용하여 맨 아래에서 읽을 수 있습니다. 이제 파일에서 n번째 줄을 가져오기 위해 head nlines , 파이프된 데이터에서 1줄만 뒤에 데이터를 파이프합니다.
TOTAL_LINES=`wc -l $USER_FILE | cut -d " " -f1 ` echo $TOTAL_LINES # To validate total lines in the file for (( i=1 ; i <= $TOTAL_LINES; i++ )) do LINE=`head -n$i $USER_FILE | tail -n1` echo $LINE done
madD7파이프를 사용할 때 서브쉘이 문제입니다.
while read line do # do something with $line here done <<<$(cat peptides.txt)
jamlee출처 : http:www.stackoverflow.com/questions/1521462/looping-through-the-content-of-a-file-in-bash