파일의 공백이 포함된 Grep 문자열

파일의 공백이 포함된 Grep 문자열
#!/bin/bash
LIST=/errors_exception.txt
cd /test
for PATTERN in `cat $LIST`
do
        for FILE in $(ls)
        do
        if zcat $FILE | grep -Fxq "$PATTERN"; then
        echo "$PATTERN found pattern in $FILE" >> output
        fi
done
done

다수의 압축된 로그 파일(.gz)을 스캔하고 내가 찾고 있는 패턴이 이 로그에 여전히 존재하는지 확인하려고 합니다.

예를 들어 위 코드에 errors_exception.txt다음이 포함되어 있다고 가정해 보겠습니다.

one 
one two three
four five
six

/test- 디렉터리에는 로그 파일이 포함되어 있습니다.

스크립트를 실행할 때 두 번째 줄 "one two three"를 한 줄로 읽지 않는 이유는 무엇입니까?

bash -x test.sh(스크립트 이름)를 실행하면 텍스트 파일에 다른 3줄이 있는 것처럼 두 번째 줄을 읽고 "one two three"를 한 줄로 표시합니다.

답변1

list=/errors_exception.txt
cd /test
while IFS= read -r pattern ; do
    for file in * ; do
        if zcat < "$file" | grep -Fxq "$pattern"; then
            echo "$pattern found pattern in $file"
        fi
    done
done <"$list" > output

노트:

  • 다음 두 줄 중 어느 것도 예상한 대로 작동하지 않습니다.

    for PATTERN in `cat $LIST`
    
    for FILE in $(ls)
    

    두 경우 모두 쉘은 예상하지 못한 단어 분리를 수행합니다. 위에 제안된 코드는 이러한 상황을 방지합니다.

  • 파일이 errors_exception.txt실제로 루트 디렉터리에 있습니까?

  • 변수를 소문자로 변환합니다. 이는 사용자가 만든 변수에 대한 규칙입니다. 이 규칙은 특정 중요한 쉘 매개변수를 실수로 무시하는 것을 방지합니다.

단어 분할에 대한 추가 정보

쉘이 실행될 때:

for PATTERN in `cat $LIST`

그것은 작동한다 cat $LIST. 이렇게 하면 공백, 탭 및 캐리지 리턴이 모두 하이픈 연결이라는 동일한 것으로 처리됩니다. 따라서 실제로 토큰화 후에 이 줄은 다음과 같습니다.

for PATTERN in one one two three four five six

그리고 for루프가 실행 되면 PATTERN1, 1, 2, 3, 4, 5, 6 순으로 할당됩니다.

실제로 원하는 것은 각 행을 행으로 처리하는 것입니다. 이것이 while read.... done<"$list"바로 이 구성이 사용되는 이유입니다. 각 루프에서 전체 줄을 읽습니다.

파일 이름에 공백이 포함된 경우 이 줄에서도 동일한 문제가 발생합니다.

for FILE in $(ls)

결과는 ls줄로 대체되며, 파일 이름에 공백, 탭 또는 캐리지 리턴(모두 유효한 문자임)이 포함된 경우 이름이 여러 부분으로 분할됩니다. 예를 들어, 빈 디렉터리에 파일을 만듭니다.

$ touch "a b c"

이제 for루프를 실행해 보세요.

$ for file in $(ls); do echo $file; done
a
b
c

for파일이 하나만 있어도 루프는 세 번 실행됩니다. 이는 파일 이름에 공백이 있고 단어 분리 후에 for루프가 세 개의 매개변수 a, b, c를 가져오기 때문입니다.

이것은 피하기 쉽습니다. 대신 사용하세요:

for file in *

쉘은 포함된 문자에 관계없이 모든 파일 이름을 그대로 유지할 수 있을 만큼 똑똑합니다.

재귀 검색

하위 디렉터리에서 gzip 압축 파일도 검색하려면 다음과 같이 bash의 globstar 기능을 사용할 수 있습니다.

list=/errors_exception.txt
cd /test
shopt -s globstar
while IFS= read -r pattern ; do
    for file in **/*.gz ; do
        if zcat < "$file" | grep -Fxq "$pattern"; then
            echo "$pattern found pattern in $file"
        fi
    done
done <"$list" > output

이것은 필요합니다 bash.

관련 정보