在Linux中合​​并2目录树而不复制?

我有两个具有相似布局的目录树,即

. |-- 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目录树来创build:

  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”,由于冲突的目录名称,我得到“文件存在”错误。

更新:您可以假定两个目录树之间没有重复的文件。

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

这将创build硬链接而不是移动它们,您可以validation它们是否正确移动,然后删除dir1/dir2/

奇怪的是,没有人注意到cp有选项-l

 -l, - 链接
       硬链接文件而不是复制

你可以做类似的事情

 %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个文件

你可以使用重命名(又名,来自perl包)。 注意这个名字并不一定是指我在debian / ubuntu之外描述的命令(尽pipe如果你需要的话它是一个单独的可移植的perl文件)。

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

您还可以select使用vidir(来自moreutils),并从首选文本编辑器编辑文件path。

我喜欢rsyncprename的解决scheme,但是如果你真的想让mv做这个工作和

  • 发现知道-print0-depth
  • 你的兄弟知道-0
  • 你有printf

那么可以处理大量可能在其名称中都有随机空格的文件,所有这些都使用Bourne风格的shell脚本:

 #!/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 

蛮力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 

testing做到这一点

 # 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 

在不同的开发阶段,我不得不这样做几次源代码树。 我的解决scheme是以如下方式使用Git:

  1. 创build一个git仓库并添加dir1中的所有文件。
  2. 承诺
  3. 删除所有文件并从dir2复制文件
  4. 承诺
  5. 查看两个提交点之间的差异,并仔细决定如何合并结果。

你可以用分支等等来巧妙地devise它,但是这是一般的想法。 而且由于每个状态都有完整的快照,所以你不用担心填充它。