복사하지 않고 Linux에서 2 개의 디렉토리 트리를 병합 하시겠습니까?


35

비슷한 레이아웃을 가진 두 개의 디렉토리 트리가 있습니다.

.
 |-- dir1
 |   |-- a
 |   |   |-- file1.txt
 |   |   `-- file2.txt
 |   |-- b
 |   |   `-- file3.txt
 |   `-- c
 |       `-- file4.txt
 `-- dir2
     |-- a
     |   |-- file5.txt
     |   `-- file6.txt
     |-- b
     |   |-- file7.txt
     |   `-- file8.txt
     `-- c
         |-- file10.txt
         `-- file9.txt

dir1 및 dir2 디렉토리 트리를 병합하여 작성하고 싶습니다.

 merged/
 |-- a
 |   |-- file1.txt
 |   |-- file2.txt
 |   |-- file5.txt
 |   `-- file6.txt
 |-- b
 |   |-- file3.txt
 |   |-- file7.txt
 |   `-- file8.txt
 `-- c
     |-- file10.txt
     |-- file4.txt
     `-- file9.txt

"cp"명령을 사용하여이 작업을 수행 할 수 있다는 것을 알고 있지만 병합하려는 실제 디렉토리가 실제로 크고 파일 (수백만)이 많기 때문에 복사하는 대신 파일을 이동하고 싶습니다. "mv"를 사용하면 디렉토리 이름이 충돌하여 "파일이 있습니다"오류가 발생합니다.

업데이트 : 두 디렉토리 트리 사이에 중복 파일이 없다고 가정 할 수 있습니다.


두 폴더간에 파일 이름이 중복되어 있지 않습니까? 중복이 있으면 어떻게 하시겠습니까?
Zoredache

말 그대로 단일 디렉토리에 수백만 개의 파일이있는 경우 성능상의 이유로 파일을 별도의 하위 디렉토리로 분할해야합니다. 실제 질문과는 관련이 없습니다.
DrStalker

답변:


28
rsync -ax --link-dest=dir1/ dir1/ merged/
rsync -ax --link-dest=dir2/ dir2/ merged/

이것은 오히려 그들을 이동 이상의 하드 링크 만들 것입니다, 당신은 그들이 올바르게, 다음, 제거를 이동 한 것을 확인할 수 있습니다 dir1/dir2/.


9
거의. 실제로 디스크 사용량을 복제하지 않고 동일한 디스크 덩어리에 대한 다른 포인터를 만들고 실제로 데이터를 '복사'하지 않습니다. ( en.wikipedia.org/wiki/Hard_links 참조 ) 그러나 파일 당 한 번씩 해당 작업을 수행해야합니다. 그러나 이것이 단일 디렉토리를 이동할 수 없기 때문에 본질적으로 이러한 모든 대답이 이루어집니다.
Christopher Karel

1
파일 복사에 대한 오버 헤드가 없으므로 완벽하게 수용 가능한 솔루션입니다.
Tobu

2
동일한 파일 시스템에있는 경우에만 작동합니다. 삭제 옵션을 사용하는 rsync가 동일한 파일 시스템에있는 경우 이동합니까? 즉, 디렉토리 정보 만 변경하고 파일을 이동하지는 마십시오.
Ronald Pottol

1
rsync는 파일 시스템을 통과하는 경우 복사 한 다음 삭제합니다.
karmawhore

5
한 가지주의 사항 : --link-dest경로를 절대 또는 상대 경로로 만드십시오 merged/. 또는 복사합니다.
Tobu

21

아무도 cp옵션 을 가지고 있다고 언급 한 것은 이상하지 않습니다 -l.

-l, --link
       복사하는 대신 하드 링크 파일

당신은 같은 것을 할 수 있습니다

mkdir 병합 %
% cp -rl dir1 / * dir2 / * 병합
% rm -r dir *
% 나무 병합 
합병
├── a
│ ├── file1.txt
│ ├── file2.txt
│ ├── file5.txt
│ └── file6.txt
├── b
│ ├── file3.txt
│ ├── file7.txt
│ └── file8.txt
└── c
    ├── file10.txt
    ├── file4.txt
    └── file9.txt

13 개 디렉토리, 0 개 파일

이것은 다른 하드 드라이브에서 작동하지 않습니다 ...
Alex Leach

4
파일 시스템은 여러 하드 드라이브에 걸쳐있을 수 있으므로 파일 시스템에서 작동하지 않는다고 말하는 것이 더 정확합니다. 또한 파일이 복사되는 것을 피하기 위해 파일 cp -l시스템에서 작동하지 않는 것이 좋습니다 .
lvella

2
파일의 모든 속성을 유지하고 다음과 같은 심볼릭 링크를 피하기 위해 cp -a(동의어 cp -RPp) 를 사용할 수 있습니다 cp -al dir1/* dir2/* merge.
tricasse

5

이를 위해 이름 바꾸기 (일명 펄 패키지의 사전 이름)를 사용할 수 있습니다. 이름은 필자가 debian / ubuntu 외부에서 설명하는 명령을 반드시 참조 할 필요는 없습니다 (필요한 경우 하나의 휴대용 perl 파일 임).

mv -T dir1 merged
rename 's:^dir2/:merged/:' dir2/* dir2/*/*
find dir2 -maxdepth 1 -type d -empty -delete

moreutils에서 vidir을 사용하고 원하는 텍스트 편집기에서 파일 경로를 편집 할 수도 있습니다.


3

나는 rsyncprename 솔루션을 좋아 하지만 실제로 mv 가 작업을 수행하도록하려면

  • 당신의 발견은 알고 -print0하고 -depth,
  • 당신의 xargs 는 알고 있습니다 -0.
  • 당신은 printf ,

그런 다음 Bourne 스타일의 쉘 스크립트를 사용하여 이름에 임의의 공백이있을 수있는 많은 파일을 처리 할 수 ​​있습니다.

#!/bin/sh

die() {
    printf '%s: %s\n' "${0##*/}" "$*"
    exit 127
}
maybe=''
maybe() {
    if test -z "$maybe"; then
        "$@"
    else
        printf '%s\n' "$*"
    fi
}

case "$1" in
    -h|--help)
        printf "usage: %s [-n] merge-dir src-dir [src-dir [...]]\n" "${0##*/}"
        printf "\n    Merge the <src-dir> trees into <merge-dir>.\n"
        exit 127
    ;;
    -n|--dry-run)
        maybe=NotRightNow,Thanks.; shift
    ;;
esac

test "$#" -lt 2 && die 'not enough arguments'

mergeDir="$1"; shift

if ! test -e "$mergeDir"; then
    maybe mv "$1" "$mergeDir"
    shift
else
    if ! test -d "$mergeDir"; then
        die "not a directory: $mergeDir"
    fi
fi

xtrace=''
case "$-" in *x*) xtrace=yes; esac
for srcDir; do
    (cd "$srcDir" && find . -print0) |
    xargs -0 sh -c '

        maybe() {
            if test -z "$maybe"; then
                "$@"
            else
                printf "%s\n" "$*"
            fi
        }
        xtrace="$1"; shift
        maybe="$1"; shift
        mergeDir="$1"; shift
        srcDir="$1"; shift
        test -n "$xtrace" && set -x

        for entry; do
            if test -d "$srcDir/$entry"; then
                maybe false >/dev/null && continue
                test -d "$mergeDir/$entry" || mkdir -p "$mergeDir/$entry"
                continue
            else
                maybe mv "$srcDir/$entry" "$mergeDir/$entry"
            fi
        done

    ' - "$xtrace" "$maybe" "$mergeDir" "$srcDir"
    maybe false >/dev/null ||
    find "$srcDir" -depth -type d -print0 | xargs -0 rmdir
done

xargs에게 입력을 줄 바꿈으로 구분하고 번역을 생략하도록 지시 할 수 있습니다. 예를 들어 다음은 현재 디렉토리 아래의 모든 토렌트 파일을 찾아 삭제합니다. find . -name '*.torrent' | xargs -d '\n' rm
PRS

2

무차별 대입 bash

#! /bin/bash

for f in $(find dir2 -type f)
do
  old=$(dirname $f)
  new=dir1${old##dir2}
  [ -e $new ] || mkdir $new
  mv $f $new
done

테스트는 이것을한다

# setup 
for d in dir1/{a,b,c} dir2/{a,b,c,d} ; do mkdir -p $d ;done
touch dir1/a/file{1,2} dir1/b/file{3,4} dir2/a/file{5,6} dir2/b/file{7,8} dir2/c/file{9,10} dir2/d/file11

# do it and look
$ find dir{1,2} -type f
dir1/a/file1
dir1/a/file2
dir1/a/file5
dir1/a/file6
dir1/b/file3
dir1/b/file7
dir1/b/file8
dir1/c/file4
dir1/c/file9
dir1/c/file10
dir1/d/file11

2
OP는 수백만 개의 파일을 지정했으며이 구성을 깨뜨릴 수 있습니다. 또한 공백, 줄 바꿈 등으로 파일 이름을 올바르게 처리하지 못합니다.
Chris Johnsen 2016 년

0

다른 개발 단계에서 소스 코드 트리를 위해이 작업을 여러 번 수행해야했습니다. 내 솔루션은 다음과 같은 방식으로 Git을 사용하는 것이 었습니다.

  1. 자식 저장소를 만들고 dir1에서 모든 파일을 추가하십시오.
  2. 범하다
  3. dir2에서 모든 파일을 제거하고 파일을 복사하십시오.
  4. 범하다
  5. 두 커밋 지점 간의 차이점을보고 결과를 병합하는 방법에 대해 신중하게 결정하십시오.

분기 등을 사용하여 세부 사항을 조정할 수 있지만 이것이 일반적인 아이디어입니다. 그리고 각 상태에 대한 완전한 스냅 샷을 가지고 있기 때문에이를 채우는 것에 대한 두려움이 줄어 듭니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.