如何合并具有“名称 (1)”、“名称 (1) (1)”等结构的重复文件夹

如何合并具有“名称 (1)”、“名称 (1) (1)”等结构的重复文件夹

在我的 Google Filestream、Google Drive 和 Synology CloudSync 之间同步时,一切都变得混乱了,我留下了数百个重复的文件夹,文件夹名称后跟“(1)”或“(2)”等,一直到“(1) (1) (1)”。

您知道可以合并这些文件夹的程序或脚本吗?

顶级文件夹结构示例:

1100 Beetledwarf - Happy ATE
1100 Beetledwarf - Happy ATE (1)
1100 Beetledwarf - Happy ATE (2)
1100 Beetledwarf - Happy ATE (3)
1100 Beetledwarf - Happy ATE (3) (1)
1100 Beetledwarf - Happy ATE (3) (1) (1)
1100 Beetledwarf - Happy ATE (4)
1100 Beetledwarf - Happy ATE (5)
1100 Beetledwarf - Happy ATE (6)

由于子文件夹有时也存在同样的问题,因此程序或脚本需要能够合并遵循所有子文件夹命名模式的文件夹,例如:

第二级文件夹示例:

1100 Beetledwarf - Happy ATE (6)
    Analysis
    Analysis (1)
    Smirckle_HL
    Smirckle_HL (2)
    Pending Reports
    Photos & Logos

最好的解决方案还允许我移动文件而不是复制文件,因为复制文件需要很长时间,但移动几乎是即时的。

我已经尝试过的方法列表,但它们都无法处理“名称(1)”文件夹结构(到目前为止我可以说的),并且它们都复制文件而不是移动它们:

  • Windows 10 版 WinMerge <- 尝试复制 Google Drive 文件时发生故障(返回类似“不支持 DOS 命令”的内容)
  • Meld for MacOS。<- 慢。
  • OS X 中带有“ditto”命令的终端<-迄今为止最佳选择。

感谢您的帮助!

答案1

这是我在 Linux 中尝试的方法。我没有使用过 Google Filestream、Google Drive 或 Synology CloudSync,所以我无法判断该解决方案是否可行。不过我希望这至少能给你一些启发。


假设

  • 您可以在目录树中挂载共享,因此mvcp和其他合理工具可以像处理本地目录一样处理目录;
  • 删除所有(N)字符串后路径变得相同的文件(或目录)实际上是同一个文件(目录)的实例;
  • 同一个文件的实例应该只留下一个文件;
  • 同一目录的实例应将其内容合并到一个目录中;
  • 您可以使用我在这里使用的所有工具。

程序

在尝试做任何事情之前,请阅读整个答案。

我认为有些步骤可以写成脚本,但由于解决方案是高度实验性,最好是手工一步一步地做,注意发生的情况。

  1. 在 shell 中cd进入挂载点并调用find . | vidir -;使用您选择的文本编辑器,例如kate,如下所示:

    find . | EDITOR=kate vidir
    

    这将打开编辑器,其中包含所有对象的列表,每个对象前面都有自己的编号。当您更改内容并保存(临时)文件并关闭编辑器时,所有更改都会应用。一般来说,您可以执行以下操作:

    • 改变路径来移动(重命名)文件或目录;
    • 删除行以删除文件或目录;
    • 交换两个或多个数字来交换文件(你不需要它)。

    除非您确定新内容描述了您想要获取的目录树,否则不要保存文件。

  2. 将内容从编辑器复制到其他文件。关键是要使用它,只有当您确定正确时才粘贴结果(并保存)。除非另有明确说明,否则后续步骤将参考新文件。

  3. 使用sed或任何其他工具删除所有(N)字符串(注意前导空格)。此时您应该获得“干净”的路径,其中许多路径会出现多次(由 给出不同的数字vidir)。

  4. 使用sort -k 2根据这些路径进行排序。由于-s前者Analysis仍然应该在前者之前Analysis (1)

  5. 用于uniq -f 1删除重复的路径。现在任何路径都只应出现一次。

  6. 仔细检查结果中编码的目录结构的健全性。

  7. 将结果粘贴到原始编辑器中,保存文件并退出编辑器。vidir将删除与缺失数字相关的对象并移动与剩余数字相关的对象。


测试

我会首先使用此解决方案复制目录结构:

cp -a --attributes-only /mountpoint/ /guinea_pig_dir/

并在生成的空文件上测试该过程。这应该会发现问题(如果有的话),并有望改进该方法。


可能出现的问题

  1. vidir拒绝使用一些非标准字符。

  2. 一般来说,对象的顺序很重要。当与发生冲突时,生成像foo~或 这样的对象时foo~1,会出现一些陷阱。您将以一种不会产生冲突的方式“收缩”目录树,但我还没有调查所有可能的情况。我真的认为你应该尝试一下,看看会得到什么。如果遇到麻烦foo~2foo/guinea_pig_dir/或许巧妙的sort介于find和之间vidir将会有所帮助。

答案2

下面是执行此任务的 bash 脚本。它适用于添加了 rsync 的 MSYS2 Bash。它取自此处的相关问题:

用于对具有特定后缀的文件和文件夹进行重复数据删除的脚本

#!/usr/bin/bash
IFS=$'\n';
set -f
#Go deepest first to deal with copies within copied folders.
for copy in $(find . -regextype posix-egrep -regex "^.*\ \([0-9]+\)\s*(\.[^/.]*)?$" | awk '{print length($0)"\t"$0}' | sort -rnk1 | cut -f2-); do
    orig=$(rev <<< "$copy" | sed -E 's/\)[0-9]+\(\ //' | rev)
    if [ "$orig" != "$copy" ]; then
        if [ -f "$orig" ]; then
            if [ -f "$copy" ]; then
                echo "File pair: $orig $copy"
                if diff -q "$orig" "$copy" &>/dev/null; then
                    echo "Removing file: $copy"
                    rm -f "$copy";
                fi
            fi           
        fi
        if [ -d "$orig" ]; then
            if [ -d "$copy" ]; then
                echo "Folder pair: $orig $copy"
                if rmdir "$copy" &>/dev/null; then
                    #If the "copy" was an empty directory then we've removed it and so we're done.
                    echo "Removed empty folder: $copy"
                else
                    #Non-destructively ensure that both folders have the same files at least.                    
                    rsync -aHAv --ignore-existing "$orig/" "$copy" &>/dev/null
                    rsync -aHAv --ignore-existing "$copy/" "$orig" &>/dev/null
                    if diff -qr "$orig" "$copy" &>/dev/null; then
                        echo "Removing folder: $copy"
                        rm -rf "$copy";
                    fi            
                fi
            fi
        fi
    fi
done
unset IFS;
set +f

答案3

以下脚本可以在 OS X 上运行,但存在一些问题:

有时 Google 文档无法复制,因此文件夹无法通过差异测试,因此不会被删除。然后我必须手动对它们运行差异测试,检查差异是否都是 Google 文档,如果 Google 文档不在原始文件夹中,则手动将 Google 文档移动到原始文件夹中,然后手动删除重复的文件夹。(注意:从 Finder 中我无法复制相同的文档,这对我来说似乎很奇怪,因为我觉得我以前复制过(或选择 + 拖动)Google 文档文件。)

#!/usr/bin/bash
IFS=$'\n';
set -f
#Go deepest first to deal with copies within copied folders.
for copy in $(find -E . -regex "^.*\ \([0-9]+\)\s*(\.[^/.]*)?$" | awk '{print length($0)"\t"$0}' | sort -rnk1 | cut -f2-); do
    orig=$(rev <<< "$copy" | sed -E 's/\)[0-9]+\(\ //' | rev)
    if [ "$orig" != "$copy" ]; then
        if [ -f "$orig" ]; then
            if [ -f "$copy" ]; then
                echo "File pair: $orig $copy"
                if diff -q "$orig" "$copy" &>/dev/null; then
                    echo "Removing file: $copy"
                    rm -f "$copy";
                fi
            fi           
        fi
        if [ -d "$orig" ]; then
            if [ -d "$copy" ]; then
                echo "Folder pair: $orig $copy"
                if rmdir "$copy" &>/dev/null; then
                    #If the "copy" was an empty directory then we've removed it and so we're done.
                    echo "Removed empty folder: $copy"
                else
                    #Non-destructively ensure that both folders have the same files at least.                    
                    rsync -aHAv --ignore-existing "$orig/" "$copy" &>/dev/null
                    rsync -aHAv --ignore-existing "$copy/" "$orig" &>/dev/null
                    if diff -x ‘.*’ -x 'Icon?' -qr "$orig" "$copy" &>/dev/null; then
                        echo "Removing folder: $copy"
                        trash -v "$copy"      # requires that Ali Rantakari's app is installed: aka that you have already run 'brew install trash'
                        #replaced the following: rm -rf "$copy";
                    fi            
                fi
            fi
        fi
    fi
done
unset IFS;
set +f

注意:此项工作可能依赖于安装以下工具,详见本页:https://www.topbug.ne​​t/blog/2013/04/14/install-and-use-gnu-command-line-tools-in-mac-os-x/

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install coreutils
export PATH="$(brew --prefix coreutils)/libexec/gnubin:/usr/local/bin:$PATH"
brew tap homebrew/dupes
brew install binutils
brew install diffutils
brew install ed --with-default-names
brew install findutils --with-default-names
brew install gawk
brew install gnu-indent --with-default-names
brew install gnu-sed --with-default-names
brew install gnu-tar --with-default-names
brew install gnu-which --with-default-names
brew install gnutls
brew install grep --with-default-names
brew install gzip
brew install screen
brew install watch
brew install wdiff --with-gettext
brew install wget
brew install bash
brew install rsync

相关内容