it-swarm-korea.com

Bash에서 파일 내용을 반복하기

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로 화면에 출력하는 것보다 더 복잡한 작업을 수행하려고합니다.)


환경 변수는 (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 ([email protected]) (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
1126
Peter Mortensen

이를 수행하는 한 가지 방법은 다음과 같습니다.

while read p; do
  echo "$p"
done <peptides.txt

주석에서 지적했듯이, 이것은 선행 공백을 제거하고, 백 슬래시 시퀀스를 해석하며, 종료 라인 피드가없는 경우 후행 라인을 건너 뛰는 부작용이 있습니다. 문제가되는 경우 다음을 수행 할 수 있습니다.

while IFS="" read -r p || [ -n "$p" ]
do
  printf '%s\n' "$p"
done < peptides.txt

예외적으로 루프 본문이 표준 입력에서 읽을 수 있음 을 사용하면 다른 파일 설명자를 사용하여 파일을 열 수 있습니다.

while read -u 10 p; do
  ...
done 10<peptides.txt

여기서 10은 임의의 숫자입니다 (0, 1, 2와는 다릅니다).

1786
Bruno De Fraine
cat peptides.txt | while read line
do
   # do something with $line here
done
345
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

옵션 2 : For 루프 : 파일을 단일 변수로 읽고 구문 분석합니다.
이 구문은 토큰 사이의 공백을 기반으로 "선"을 구문 분석합니다. 주어진 입력 파일 행이 단일 Word 토큰이기 때문에 여전히 작동합니다. 한 줄에 하나 이상의 토큰이있는 경우이 방법은 작동하지 않습니다. 또한 전체 파일을 단일 변수로 읽는 것은 대용량 파일에 적합한 전략이 아닙니다.

#!/bin/bash
filename='peptides.txt'
filelines=`cat $filename`
echo Start
for line in $filelines ; do
    echo $line
done
130
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

이것은 셸에게 공백이 아닌 줄 바꿈 전용으로 분할하도록 지시 한 다음 이전의 환경으로 돌아갑니다. 이 시점에서 쉘 스크립트에 모든 것을 넣는 것을 고려해 볼 수 있습니다.하지만 모든 것을 한 줄로 짜내는 것이 좋습니다.

행운을 빕니다!

69
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 루프는 후자의 경우처럼 하위 쉘보다는 현재 쉘에서 실행되기 때문입니다. 관련 게시물을 참조하십시오 잠시 동안 수정 된 변수는 루프가 기억되지 않습니다 .

find ... -print0와 같이 null로 구분 된 입력을 읽음

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의 answer here :에 근거하여

-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

관련 게시물:

56
codeforester

다음과 같이 while 루프를 사용하십시오.

while IFS= read -r line; do
   echo "$line"
done <file

노트:

  1. IFS을 올바르게 설정하지 않으면 들여 쓰기가 손실됩니다.

  2. 거의 항상 읽기에는 -r 옵션을 사용해야합니다.

  3. for 행을 읽지 않습니다.

42
Jahid

당신의 개행 문자가 개행 문자로 깨어지기를 원하지 않는다면 -

#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
    echo "$line"
done < "$1"

그런 다음 매개 변수로 파일 이름을 사용하여 스크립트를 실행하십시오.

13
Anjul Sharma

이 파일을 가지고 있다고 가정 해보십시오.

$ 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 솔루션이 읽는 파일 출력의 의미를 변경하는 네 가지 요소가 있습니다.

  1. 빈 줄 4;
  2. 두 줄의 선행 또는 후행 공백;
  3. 개별 라인의 의미 유지 (즉, 각 라인은 레코드 임);
  4. 6 번 줄은 CR로 끝나지 않았습니다.

빈 줄과 CR이없는 줄을 포함하여 한 줄씩 텍스트 파일을 원할 경우 while 루프를 사용해야하며 마지막 줄에 대해 대체 테스트가 있어야합니다.

다음은 cat이 반환하는 것과 비교하여 파일을 변경할 수있는 메소드입니다.

1) 마지막 줄과 앞뒤 공백을 잃습니다.

$ while read -r p; 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 -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt를 수행하면 선행 공백과 후행 공백은 유지되지만 CR로 종료되지 않은 경우 마지막 행은 손실됩니다)

2) 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)에서 "를 제거하면 꿀꺽 꿀꺽 말고 Word에서 파일을 읽습니다. 의도하지 않은 내용 일 수도 있습니다 ...)


파일을 한 행씩 읽고 모든 간격을 유지하는 가장 강력하고 간단한 방법은 다음과 같습니다.

$ 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

12
dawg
#!/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
4
Sine

여기에 다른 프로그램 출력의 줄을 반복하는 방법, 하위 문자열을 검사하는 방법, 변수에서 큰 따옴표를 빼는 방법, 루프 외부에서 해당 변수를 사용하는 방법을 보여주는 실제 예제가 있습니다. 나는 많은 사람들이 조만간 이러한 질문을하고있는 것으로 생각한다.

##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 <<< "$ (...)"구문이 필요합니다. 응용 프로그램은 현재 콘솔의 컨텍스트 내에서 실행되어야합니다. 명령 주위의 인용문은 출력 스트림의 개행을 유지합니다.

하위 문자열에 대한 루프 일치는 name = value pair를 읽거나, 마지막 = 문자의 오른쪽 부분을 분리하고, 첫 번째 인용 부호를 삭제하고, 마지막 인용 부호를 삭제합니다. 다른 곳에 사용될 깨끗한 값을 갖습니다.

3
Whome

@ 피터 : 이것은 당신을 위해 운동 할 수 있습니다 -

echo "Start!";for p in $(cat ./pep); do
echo $p
done

이렇게하면 출력 -

Start!
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL
1
Alan Jebakumar

이것은 다소 늦어 지지만 누군가를 도울 수 있다는 생각으로 대답을 추가하고 있습니다. 또한 이것이 최선의 방법이 아닐 수도 있습니다. head 명령을 -n 인수와 함께 사용하여 파일 시작에서 n 줄 을 읽을 수 있습니다. 마찬가지로 tail 명령을 사용하여 밑에서 읽을 수 있습니다. 이제 파일에서 nth 줄을 가져 오려면 n 줄 파이프 된 데이터에서 한 줄만 데이터를 파이프합니다.

   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
0
madD7