linux - 특정 - Bash에서 파일 내용을 반복하기




쉘 스크립트 파일 읽기 변수 저장 (8)

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 ([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

@ 피터 : 이것은 당신을 위해 밖으로 일할 수 있습니다 -

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

이렇게하면 출력 -

Start!
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL

다른 답변에 포함되지 않은 몇 가지 사항 :

구분 된 파일에서 읽기

# ':' 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 ... 보다 command ... | while read -r line; do ... command ... | while read -r line; do ... command ... | while read -r line; do ... while 루프는 여기 에서처럼 후자의 경우처럼 서브 쉘보다는 현재 쉘에서 실행되기 때문입니다. 관련 게시물보기 while 루프 내부에서 수정 된 변수는 기억되지 않습니다 .

널로 구분 된 입력을 읽습니다 (예 : 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's 대답을 바탕으로 :

-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

관련 게시물:

  • Bash에서 텍스트 파일로 배열 만들기
  • 한 줄의 파일을 읽는 방법의 차이점은 무엇입니까?
  • 고양이와 비교했을 때 매우 느린 읽기 반복 동안 Bash는, 왜?

다음은 다른 프로그램 출력의 줄을 반복하고, 하위 문자열을 확인하고, 변수에서 큰 따옴표를 제거하고, 해당 변수를 루프 외부에서 사용하는 실제 예제입니다. 나는 많은 사람들이 조만간 이러한 질문을하고있는 것으로 생각한다.

##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 쌍을 읽고 마지막 문자의 오른쪽 부분을 분할하고 첫 번째 인용 부호를 삭제하고 마지막 인용 부호를 삭제합니다. 다른 곳에서 사용할 깨끗한 값이 있습니다.


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

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

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


이것은 다른 대답보다 나은 것은 아니지만 공백없이 파일에서 작업을 완료하는 또 다른 방법입니다 (주석 참조). 필자는 별도의 스크립트 파일을 사용하는 별도의 단계없이 텍스트 파일의 목록을 파헤 치기 위해 종종 한 줄짜리 코드가 필요하다는 것을 알게되었습니다.

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

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

행운을 빕니다!


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

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와는 다릅니다).


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

cat peptides.txt | while read line
do
   # do something with $line here
done




io