(유사한 질문이 많이 있다는 점에 유의하세요(예:여기,여기,여기, 그리고여기) 그러나 그들은 디렉토리 구조가 한 수준이라고 가정하거나 대답은 더 복잡한 여러 줄 스크립트라고 가정합니다. )
내 상황은 다음과 같습니다.
.
├── 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
이전 시도에서 무엇이 잘못되었는지 아직 모르기 때문에 이 답변을 받아들이지 않겠습니다.