문자열 목록이 있고 각 문자열에 대해 그것이 큰 소스 코드 디렉토리에 나타나는지 확인하고 싶습니다.
내가 원하는 것을 제공하는 GNU grep 솔루션을 찾았습니다.
for key in $(cat /tmp/listOfKeys.txt); do
if [ "$(grep -rio -m 1 "$key" . | wc -l)" = "0" ]; then
echo "$key has no occurence";
fi
done
그러나 일치하는 항목을 조기에 찾더라도 항상 디렉터리 아래의 모든 파일을 grep하기 때문에 비효율적입니다. 찾아야 할 키도 많고, 검색해야 할 파일도 너무 많기 때문에 그대로 작동하지 않습니다.
"표준" 유닉스 도구를 사용하여 이를 효율적으로 수행하는 방법을 알고 있습니까?
답변1
최소한 다음과 같이 단순화할 수 있습니다.
set -f # needed if you're using the split+glob operator and don't want the
# glob part
for key in $(cat /tmp/listOfKeys.txt); do
grep -riFqe "$key" . ||
printf '%s\n' "$key has no occurrence"
done
이렇게 하면 첫 번째 발생 후 검색이 중지되고 key
키를 정규식( 또는 에 가능한 옵션 grep
)으로 간주하지 않습니다.
파일을 여러 번 읽는 것을 방지하고 키 목록이 한 줄에 하나의 키라고 가정하려면(위 코드에서처럼 구분된 공백/탭/줄바꿈 대신) GNU 도구를 사용할 수 있습니다.
find . -type f -size +0 -printf '%p\0' | awk '
ARGIND == 2 {ARGV[ARGC++] = $0; next}
ARGIND == 4 {a[tolower($0)]; n++; next}
{
l = tolower($0)
for (i in a) if (index(l, i)) {
delete a[i]
if (!--n) exit
}
}
END {
for (i in a) print i, "has no occurrence"
}' RS='\0' - RS='\n' /tmp/listOfKeys.txt
key
a가 보이면 검색을 중지하고, 모든 키를 찾으면 중지하고, 파일을 한 번만 읽도록 최적화되어 있습니다 .
키가 에 있다고 가정합니다 listOfKeys.txt
. 키는 소문자로 출력됩니다.
위의 GNUism은 NUL로 구분된 레코드를 처리하는 기능 -printf '%p\0'
과 같습니다 . 처음 두 가지 문제는 다음을 통해 해결될 수 있습니다.ARGIND
awk
find . -type f -size +0 -exec printf '%s\0' {} + | awk '
step == 1 {ARGV[ARGC++] = $0; next}
step == 2 {a[tolower($0)]; n++; next}
{
l = tolower($0)
for (i in a) if (index(l, i)) {
delete a[i]
if (!--n) exit
}
}
END {
for (i in a) print i, "has no occurrence"
}' step=1 RS='\0' - step=2 RS='\n' /tmp/listOfKeys.txt step=3
세 번째 문제도 비슷한 트릭을 사용하여 해결할 수 있습니다.이것, 그러나 아마도 노력할 가치가 없을 것입니다. 바라보다맨발 IO 솔루션문제를 완전히 우회할 수 있는 방법을 찾고 있습니다.
답변2
GNU grep(및 내가 알고 있는 대부분의 변형)은 -f
정확히 필요한 작업을 수행하는 옵션을 제공합니다. 이 fgrep
변형은 입력 줄을 정규식 대신 일반 문자열로 처리합니다.
fgrep -rio -f /tmp/listOfKeys.txt .
일치하는 항목이 하나 이상 있는지 테스트하려면 -q
옵션을 추가하세요. Stéphane의 의견을 바탕으로 어떤 문자열이 필요한지 알아야 하는 경우아니요찾은 후에는 -h
옵션을 추가한 다음 다음 일반적인 awk 관용구를 통해 파이프합니다.
fgrep -h -rio -f /tmp/listOfKeys.txt . |
awk '{$0=tolower($0)}; !seen[$0]++' |
fgrep -v -i -x -f - /tmp/listOfKeys.txt
두 번째는 fgrep
이제 첫 번째(대소문자를 구분하지 않는 고유한 출력)의 출력을 가져와 fgrep
의미를 바꾸고 키 파일에 일치하지 않는 문자열을 표시합니다.
답변3
Stéphane Chazelas의 gawk 메소드에 대한 이식 가능한 POSIX 호환 번역:
find . -type f -exec cat {} + |
awk '
FNR==NR {keys[tolower($0)]; n++; next}
{
s = tolower($0)
for (k in keys)
if (index(s, k)) {
delete keys[k]
if (!--n)
exit
}
}
END {
for (k in keys) print k, "has no occurrence"
}
' /tmp/listOfKeys.txt -
이름이 항상 내용보다 길다는 점에서 소스 파일이 특이하지 않은 한, 파이프를 통해 전송되는 데이터가 적기 때문에 Stéphane의 솔루션이 더 효율적입니다(여기에는 커널을 통해 두 프로세스의 버퍼 간 복사가 포함됩니다).