다른 (하위) 파일 및 디렉터리 집합을 제외하고 한 집합의 파일 및 디렉터리를 이동하는 방법

다른 (하위) 파일 및 디렉터리 집합을 제외하고 한 집합의 파일 및 디렉터리를 이동하는 방법

두 개의 bash 배열이 있는데 하나(이름 toMove)에는 파일과 디렉터리에 대한 경로가 포함되어 있습니다.이동(복사 아님)다른 위치에는 exclude이동에서 제외할 파일 및 디렉터리의 경로가 포함된 또 다른 파일( )이 포함되어 있습니다.

toMove=(
    tree/subtree1
    tree/subtree2
    tree/subtree3/leaffile3
)
exclude=(
    tree/subtree1/leafdir1
    tree/subtree2/leaffile2
)

# move code ?

테스트 디렉터리 구조는 다음과 같습니다.

mkdir -p tree/{subtree1/leafdir1,subtree2/leafdir2,subtree3/leafdir3}
touch tree/{subtree1/leaffile1,subtree2/leaffile2,subtree3/leaffile3}
tree tree
    tree
    ├── subtree1
    │   ├── leafdir1
    │   └── leaffile1
    ├── subtree2
    │   ├── leafdir2
    │   └── leaffile2
    └── subtree3
        ├── leafdir3
        └── leaffile3

이사 후 예상되는 사항:

tree tree
    tree
    ├── subtree1
    │   └── leafdir1
    ├── subtree2
    │   └── leaffile2
    └── subtree3
        └── leafdir3

tree destination
    destination
    ├── subtree1
    │   └── leaffile1
    ├── subtree2
    │   └── leafdir2
    └── subtree3
        └── leaffile3

(이것과 결합하여 ) 내가 원하는 것을 정확하게 수행하는 rsync옵션 이 있지만 불행히도--exclude-from=--remove-source-filesrsync 파일을 복사하지만 나한테는 필요해이동하다그들을(성능상의 이유로) 동일한 파일 시스템에 있는 경우.

내가 생각해낸 해결책은 find를 사용하여 toMove 배열의 모든 경로(디렉토리 내용 포함) 목록을 가져오고, 이 목록을 반복하고, 제외 배열의 경로로 시작하는 모든 경로를 필터링하는 것입니다. 이것이 문제를 해결하는 올바른 방법입니까, 아니면 이 문제를 해결하는 더 간단하고 우아한 방법(일부 표준 유틸리티 사용)이 있습니까?

고쳐 쓰다:

문제는 처음 볼 때 생각하는 것만큼 사소하지 않으며 질문의 표현도 좋지 않은 것으로 밝혀졌습니다. "다른 경로의 경로를 필터링하고 영향을 받지 않은 전체 트리를 보호하는 것"에 관한 것이어야 합니다.

나는 빈 리프 디렉토리를 유지하지 않지만 아래 솔루션으로 끝났으며 모든 파일을 이동하고 어떤 경우에는 충분할 때 부모 트리만 이동한다는 두 번째 단점이 있습니다.

...
comm \
    -23 \
    <(find "${toMove[@]}"  ! -type d 2>/dev/null | sort -u || true) \
    <(find "${exclude[@]}" ! -type d 2>/dev/null | sort -u || true) |
        parallel -j "$(nproc)" -- moveFile {}
...

(moveFile은 이동을 처리하는 데 사용되는 내보낸 bash 함수입니다. 스크립트가 파일 시스템에서 실행되지 않을 때 작업 속도를 높이려면(몇 번) 병렬을 사용하세요.

Stefan Chazeras의 답변zsh제가 예전에 몰랐던 것들을 많이 알려주셨고, 선택권이 주어진다면 이 길이 좋을 것 같습니다.

답변1

모든 소스와 대상이 동일한 파일 시스템에 있는 경우 move는 a이며 기본적으로 rename()+와 동일합니다.link()unlink()

표준(광범위하지는 않지만) pax명령, (이전의 표준) 명령 및 cpioGNU 구현 with with 는 디렉토리 구조를 기호 링크로 복사할 수 있습니다(여전히 재생성되어야 하는 디렉토리에는 적용되지 않음). ( / ) 및 ( / 옵션 )을 사용하여 GNU 구현을 수행할 수 있습니다.cp-alzshbashcpio-0--nullrm-d--dir

#! /bin/zsh -
src=tree
dst=destination
toMove=(
  subtree1
  subtree2
  subtree3/leaffile3
)
exclude=(
  subtree1/leafdir1
  subtree2/leaffile2
)

set -o extendedglob
autoload zargs

src=$src:P
dst=$dst:P

cd -- $src || exit

allToMove=( $^toMove{,/**/*}~(${(~j[|])exclude})(|/*)(ND) )
print -rNC1 -- $allToMove |
  cpio --pass-through --null --link --make-directories -- $dst &&
  zargs -r -- ${(Oa)allToMove} -- rm -d --

우리는 방금 이름을 바꿀 수 있었던 디렉터리까지 포함하여 모든 대상 디렉터리를 다시 생성하고 그 안에 있는 모든 파일을 연결하게 되므로 여전히 개선이 될 수 있지만 적어도 디렉터리가 아닌 파일에 대한 데이터는 복사되지 않았습니다.

--make-directories이동할 목록에 포함되지 않아 생성된 디렉터리(예: 디렉터리)에는 subtree3소스에서 해당 메타데이터(소유권, 권한 등)가 복사되지 않습니다.

디렉터리가 비어 있지 않기 때문에 디렉터리를 삭제할 수 없다는 경고가 표시됩니다.

사용되는 zsh 기능 중 일부는 다음과 같습니다.

  • $var:P$var마치 표준을 사용하는 것처럼 저장된 파일의 절대 표준 경로로 확장됩니다 ( realpath()입력할 때 필요하므로 이후 상대 경로는 더 이상 동일한 파일을 참조하지 않습니다).$dstcd$srcdestination
  • $^array/x/ 와 같은 배열을 확장합니다 rc. 예를 들어 및 요소가 포함된 경우 대신 이 됩니다.fish$arrayABAx BxA Bx
  • A{x,y}csh와 유사한 중괄호 확장이며, , 로도 확장 됩니다 Ax.Ay
  • **/모든 수준의 하위 디렉터리와 일치합니다.
  • in 은 glob~pattern여기에서 제외를 적용하기 위해 사용된 ~and-not/Exception 연산자 입니다.extendedglob
  • (${(~j[|])exclude})(|/*)j배열 요소( |매개변수 확장 플래그로 인해 리터럴이 아닌 전역 연산자로 처리됨)를 추가하여 구성된 제외 패턴으로, 요소 또는 그 안의 파일과 일치하도록 추가합니다.|~(|/*)
  • (ND)glob 한정자는 Nullglob 및 Dotglob을 이러한 glob에 적용하므로 숨겨진 파일이 포함되고 glob이 일치하지 않아도 오류가 생성되지 않습니다.
  • print -rNC1UL로 구분된 열에 해당 매개변수 raw를 인쇄합니다.1 CN
  • ${(Oa)array}a역순 으로 확장하여 O잎이 있는 가지보다 먼저 잎이 제거되도록 합니다.
  • zargs피할 수 있는 것이 있나요?매개변수 목록이 너무 깁니다.삭제할 파일 목록이 너무 많으면 오류가 나타납니다.

관련 정보