bash 스크립트의 cmp - 기존 파일에 대해 "해당 파일 또는 디렉터리가 없습니다"

bash 스크립트의 cmp - 기존 파일에 대해 "해당 파일 또는 디렉터리가 없습니다"

디렉토리를 읽고 내용이 중복될 수 있는 파일을 식별하려고 합니다.

단순화된 버전:

#!/bin/bash

IFS=$'\n'

for FILEA in $(find "$1" -type f -print)
do
    echo "$FILEA"
    for FILEB in $(find "$1" -type f -print)
    do
        echo cmp \"${FILEA}\" \"${FILEB}\"
        cmp \"${FILEA}\" \"${FILEB}\"
    done
done

그러나 모든 cmp인스턴스가 불평합니다 No such file or directory.

cmp 명령의 에코된 버전을 얻으면 예상한 대로 정확히 작동합니다.

cmp가 스크립트 컨텍스트에서 실행될 때 파일을 볼 수 없는 이유는 무엇입니까?

답변1

주요 문제는 사용할 때 파일 이름에 리터럴 큰따옴표를 추가하는 것입니다 cmp(그리고 파일 이름 자체는 실제로 따옴표가 없습니다). 이것이 파일을 찾을 수 없는 이유입니다(이름에 따옴표가 없습니다). 출력을 반복할 수도 있습니다 find.이건 이상적이지 않아.

만약 너라면진짜사용하고 싶지 않음fdupes, 다음을 수행하여 방법을 보다 효율적으로(속도 측면에서) 만들 수 있습니다.

#!/bin/bash

# enable the "**" globbing pattern,
# remove non-matched patterns rather than keeping them unexpanded, and
# allow the matching of hidden names:
shopt -s globstar nullglob dotglob

pathnames=("${1:-.}"/**)  # all pathnames beneath "$1" (or beneath "." if "$1" is empty)

# loop over the indexes of the list of pathnames
for i in "${!pathnames[@]}"; do
    this=${pathnames[i]}  # the current pathname

    # skip this if it's not a regular file (or a symbolic link to one)
    [[ ! -f "$this" ]] && continue

    # loop over the remainder of the list
    for that in "${pathnames[@]:i+1}"; do

        # skip that if it's not a regular file (or a symbolic link to one)
        [[ ! -f "$that" ]] && continue

        # compare and print if equal
        if [[ "$this" -ef "$that" ]] || cmp -s "$this" "$that"; then
            printf '"%s" and "%s" contains the same thing\n' "$this" "$that"
        fi
    done
done

이렇게 하면 각 파일에 대해 전체 디렉터리 구조를 한 번씩 살펴보는 일이 방지되고(이 작업은 내부 루프에서 수행됨) 쌍을 여러 번 비교하는 일도 방지됩니다. 아직이야매우cmp전체 디렉터리 계층 구조의 모든 파일 조합에서 실행 해야 하기 때문에 속도가 느립니다 .

대신 더 간단한 접근 방식을 시도해 볼 수도 있습니다.

#!/bin/bash

tmpfile=$(mktemp)

find "${1:-.}" -type f -exec md5sum {} + | sort -o "$tmpfile"
awk 'FNR == NR && seen[$1]++ { next } seen[$1] > 1' "$tmpfile" "$tmpfile"

rm -f "$tmpfile"

모든 파일의 MD5 체크섬을 계산하고 목록을 정렬한 후 임시 파일에 저장합니다. 그런 다음 출력은 모든 중복 파일을 awk추출하는 데 사용됩니다 .md5sum

출력은 다음과 같습니다.

$ bash ~/script.sh
01b1688f97f94776baae85d77b06048b  ./QA/StackExchange/.git/hooks/pre-commit.sample
01b1688f97f94776baae85d77b06048b  ./Repositories/password-store.git/hooks/pre-commit.sample
036208b4a1ab4a235d75c181e685e5a3  ./QA/StackExchange/.git/info/exclude
036208b4a1ab4a235d75c181e685e5a3  ./Repositories/password-store.git/info/exclude
054f9ffb8bfe04a599751cc757226dda  ./QA/StackExchange/.git/hooks/pre-applypatch.sample
054f9ffb8bfe04a599751cc757226dda  ./Repositories/password-store.git/hooks/pre-applypatch.sample
2b7ea5cee3c49ff53d41e00785eb974c  ./QA/StackExchange/.git/hooks/post-update.sample
2b7ea5cee3c49ff53d41e00785eb974c  ./Repositories/password-store.git/hooks/post-update.sample
3c5989301dd4b949dfa1f43738a22819  ./QA/StackExchange/.git/hooks/pre-push.sample
3c5989301dd4b949dfa1f43738a22819  ./Repositories/password-store.git/hooks/pre-push.sample

위 출력에는 중복된 내용이 일부 있습니다.오른쪽파일 수.

파일 이름에 개행 문자가 포함된 경우 접두사 md5sum에 문자가 붙은 행이 출력됩니다 \.

$ touch $'hello\nworld'
$ md5sum *
\d41d8cd98f00b204e9800998ecf8427e  hello\nworld

이를 올바르게 처리하려면(줄 시작 부분에서 백슬래시를 제거하여) 스크립트의 첫 번째 파이프를 다음과 같이 수정해야 할 수도 있습니다.

find "${1:-.}" -type f -exec md5sum {} + | sed 's/^\\//' | sort -o "$tmpfile"

관련 정보