Bash: 내 파일을 읽을 때 읽기가 0이 아닌 종료 상태를 반환하는 이유는 무엇입니까?

Bash: 내 파일을 읽을 때 읽기가 0이 아닌 종료 상태를 반환하는 이유는 무엇입니까?

Bash에서 두 파일을 한 줄씩 읽고 각 줄에서 일부 작업을 수행하려고 합니다. 이것은 내 Bash 스크립트입니다.

#!/usr/bin/env bash

die()
{
    echo "$@" >&2
    exit 1
}

extract_char()
{
    echo "$1" | sed "s/.*'\([^']*\)'.*/\1/g"
}

file1=$1 # old
file2=$2 # new
counter=0

win_count=0
lose_count=0

test ! -z "$file1" || die "Please enter 2 files."
test ! -z "$file2" || die "Please enter 2 files."

while read -r line1 && read -r line2 <&3
do
    let counter++
    index=$(expr index "$line1" "'")
    if [ $index -ne 0 ]; then
        char=$(extract_char "$line1")
        char2=$(extract_char "$line2")
        test "$char" = "$char2" || die "Chars in line1 and line2 were not the same."
    elif [ "${line1#char.}" != "$line1" ]; then
        test "${line2#char.}" != "$line2" || die "Method signature found in line1, but not line2."
        method=${line1%:}
        method=${method#char.}
    elif ! grep -q '[^[:space:]]'; then
        # benchmark times
        if [ $(date --date="$line1" +%s%N) -gt $(date --date="$line2" +%s%N) ]; then
            echo "$char $method $counter: $line1 is greater than $line2"
            let lose_count++
        else
            let win_count++
        fi
    fi
done < "$file1" 3< "$file2"

echo
echo "Lines where this made an improvement: $win_count"
echo "Lines where this made a regression: $lose_count"

사용법은 다음과 같습니다.

./compare.sh oldresults.txt newresults.txt

여기서 oldresults.txtnewresults.txt는 벤치마크 결과가 포함된 두 개의 파일입니다. 다음은 예제 파일입니다:

Test results for '\u0020':

char.IsUpper:
00:00:00.1231231
00:00:00:4564564

char.IsLower:
00:00:00:3453455
00:11:22:4444444

Tests for '\u1234':

# and so on

어떤 이유로 read파일 읽기를 마치기 전에 0이 아닌 종료 상태를 반환하는 것 같습니다. 다음은 스크립트를 디버그할 때의 출력입니다(통과 bash --debug -x compare.sh [args]).

+ file1=oldresults.txt
+ file2=newresults.txt
+ counter=0
+ win_count=0
+ lose_count=0
+ test '!' -z oldresults.txt
+ test '!' -z newresults.txt
+ read -r line1
+ read -r line2
+ let counter++
++ expr index 'Test results for '\''\u0020'\'':
' \'
+ index=18
+ '[' 18 -ne 0 ']'
++ extract_char 'Test results for '\''\u0020'\'':
'
++ echo 'Test results for '\''\u0020'\'':
'
++ sed 's/.*'\''\([^'\'']*\)'\''.*/\1/g'
+ char='\u0020'
++ extract_char 'Test results for '\''\u0020'\'':
'
++ echo 'Test results for '\''\u0020'\'':
'
++ sed 's/.*'\''\([^'\'']*\)'\''.*/\1/g'
+ char2='\u0020'
+ test '\u0020' = '\u0020'
+ read -r line1
+ read -r line2
+ let counter++
++ expr index $'\r' \'
+ index=0
+ '[' 0 -ne 0 ']'
+ '[' $'\r' '!=' $'\r' ']'
+ grep -q '[^[:space:]]'
+ read -r line1 # exits the loop here
+ echo

+ echo 'Lines where this made an improvement: 0'
Lines where this made an improvement: 0
+ echo 'Lines where this made a regression: 0'
Lines where this made a regression: 0

보시다시피 스크립트는 두 줄 \u0020, 즉 따옴표 사이에서 추출된 "...에 대한 테스트 결과" 줄과 캐리지 리턴을 반복합니다. 그 후에는 read -r line1이상하게도 실패하고 루프를 종료하는 것 같습니다.

왜 이런 일이 발생합니까? 이 문제를 해결하려면 어떻게 해야 합니까? 감사해요.

답변1

무슨 일이 일어나고 있는 거야?grep -q '[^[:space:]]'표준 입력의 나머지 라인 처리( grep입력을 제공하지 않으면 기본적으로 수행됨) 다음 단계에서는 아무것도 남기지 않습니다. read파일 포인터는 EOF에 있습니다. 당신이 원하는 것은 입니다 grep -q '[^[:space:]]' <<< "$line1".

이 오류를 방지하는 간단한 방법은 루핑 코드가 중요한 경우 항상 기본이 아닌 파일 설명자를 사용하는 것입니다. 단일 명령으로 stdin을 모두 삼키는 방법이 있지만 기본적으로 FD 3 이상을 읽으려고 시도하는 프로그램은 아직 본 적이 없습니다.

관련 정보