다른 디렉터리에서 중복 항목을 반복적으로 제거

다른 디렉터리에서 중복 항목을 반복적으로 제거

(유사한 질문이 많이 있다는 점에 유의하세요(예:여기,여기,여기, 그리고여기) 그러나 그들은 디렉토리 구조가 한 수준이라고 가정하거나 대답은 더 복잡한 여러 줄 스크립트라고 가정합니다. )

내 상황은 다음과 같습니다.

.
├── to_keep
│   ├── a
│   │   └── duplicate1.txt
│   └── b
│       ├── duplicate2.txt
│       └── unique1.txt
└── to_purge
    ├── c
    │   └── duplicate1.txt
    └── d
        ├── duplicate2.txt
        └── unique2.txt

to_keep(및 하위 디렉터리)에 있는 기본 이름을 가져와 to_purge(및 해당 하위 디렉터리)에서 동일한 이름을 가진 파일을 제거하는 간단한 한 줄 스크립트가 있습니까 ?

내 시도는 모두 실패했습니다.

(두 경우 모두 명령이 작동하는 상태 find -print로 전환하는 것을 목표로 명령을 테스트하는 데 사용했습니다.)find -delete

처음 사용 $():

find ./to_purge/ -print -name $(find ./to_keep/ -type f -printf "%f\n")
find: paths must precede expression: `duplicate2.txt'

두 번째 사용 xargs:

find ./to_keep/ -type f -printf "%f\n" | xargs --max-args=1 find ./to_purge/ -print -name
./to_purge/
./to_purge/c
./to_purge/c/duplicate1.txt
./to_purge/d
./to_purge/d/duplicate2.txt
./to_purge/d/unique2.txt
./to_purge/
./to_purge/c
./to_purge/c/duplicate1.txt
./to_purge/d
./to_purge/d/duplicate2.txt
./to_purge/d/unique2.txt
./to_purge/
./to_purge/c
./to_purge/c/duplicate1.txt
./to_purge/d
./to_purge/d/duplicate2.txt
./to_purge/d/unique2.txt

두 시도 모두 작동하지 않습니다. 나한테 무슨 문제라도 있는 걸까?

답변1

다음은 그 안이나 그 아래에 있는 모든 일반 파일을 찾고 ./to_keep이러한 파일에 대한 인라인 스크립트를 일괄 호출합니다. 각 경로 이름 배치에 대해 인라인 스크립트는 그 아래에 동일한 이름을 가진 일반 파일을 sh -c찾기 위해 한 번 호출됩니다 . 아래 파일의 경로 이름이 find인쇄됩니다 ./to_purge(제거하려면 나중에 추가하세요)../to_purge-delete-print

find to_keep -type f -exec sh -c '
    for pathname do
        set -- "$@" -o -name "${pathname##*/}"
        shift
    done; shift
    find to_purge \( "$@" \) -type f -print' sh {} +

또는 요청에 따라 한 줄로 작성합니다.

find to_keep -type f -exec sh -c 'for pathname do set -- "$@" -o -name "${pathname##*/}"; shift; done; shift; find to_purge \( "$@" \) -type f -print' sh {} +

인라인 스크립트는 마지막 줄에 사용된 명령에 대한 OR 테스트 목록을 구성합니다 -name. 루프는 외부적으로 전달된 각 경로 이름의 파일 이름 부분을 기반으로 find위치 인수로 이 목록을 구성합니다 .find

여기에는 공백, 탭, 개행 문자를 포함하여 허용되는 모든 파일 이름이 포함됩니다. 다시 한 번삭제파일의 경우 코드 뒤에 -delete(또는 )을 추가하세요 -exec rm {} +.-print

"디렉토리 유지" 및 "디렉토리 지우기"를 명령줄 인수로 사용하는 짧은 스크립트:

#!/bin/sh

keepdir=$1
purgedir=$2

find "$keepdir" -type f -exec sh -c '
    dir=$1; shift
    for pathname do
        set -- "$@" -o -name "${pathname##*/}"
        shift
    done; shift
    find "$dir" \( "$@" \) -type f -print' sh "$purgedir" {} +

이 코드의 유일한 문제점은 디렉토리의 이름을 다음과 같이 사용한다는 것입니다.무늬다른 디렉터리에서 파일 이름을 찾는 데 사용됩니다. 이는 첫 번째 디렉터리의 파일이 호출되면 *두 번째 디렉터리의 모든 파일이 삭제된다는 의미입니다. 내부 파일 이름 보호 문제를 해결할 수 있습니다 find.

for pathname do
    sane=$( printf "%s\n" "${pathname##*/}" | sed "s/[[*?]/\\&/g" )
    set -- "$@" -o -name "$sane"
    shift
done; shift

인라인 스크립트 sh -c의 루프를 수정하면 [, *?문자가 이스케이프됩니다(그렇지 않으면 파일 이름 글로빙 패턴으로 사용됨). 이제 스크립트는 다음 파일 이름을 처리하지 않습니다.줄 바꿈(명령 대체으로 인해)이지만 아마도 사람들이 사용할 수 있는 것일 것입니다.

답변2

일반적으로 게시되자마자 답변을 찾았습니다!

find ./to_keep/ -type f -exec basename '{}' \; | xargs --max-args=1 find ./to_purge/ -name | xargs --max-args=1 rm

이전 시도에서 무엇이 잘못되었는지 아직 모르기 때문에 이 답변을 받아들이지 않겠습니다.

관련 정보