#!/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
루프가 실행 되면 PATTERN
1, 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
.