展平嵌套目录


Answers:


75

您可以使用GNU find和GNU 来做到这一点mv

find /dir1 -mindepth 2 -type f -exec mv -t /dir1 -i '{}' +

基本上,如果find遍历整个目录树并且对于-type f不在顶级目录(-mindepth 2)中的每个文件()进行操作,它将运行a mv来将其移动到所需的目录(-exec mv … +)。该-t参数mv可以让你先指定目标目录,这是必须的,因为+形式-exec将所有在命令结束的源位置。该-i品牌mv覆盖任何重复之前询问; 您可以替代-f不要求(或-n不要求或覆盖)而覆盖它们。

正如Stephane Chazelas指出的那样,以上内容仅适用于GNU工具(Linux上是标准工具,但其他大多数系统却不适用)。以下内容会稍微慢一些(因为它会被mv多次调用),但是更通用一些:

find /dir1 -mindepth 2 -type f -exec mv -i '{}' /dir1 ';'

3
经过编辑使用-exec +,使其不会执行大量的处理mv
Random832

1
@ Random832并再次还原,因为+无效。mv需要将目的地作为最后一个参数,但+会将源作为最后一个参数。查找甚至不会接受将其更改为(find: missing argument to `-exec')的语法
derobert 2012年

1
@ Random832,但我想mv有一个-t可以使用的,所以我将其更改为该名称。
derobert 2012年

1
@Dom find默认情况下打印隐藏的(点)文件。深度与您要查找的目录有关。
derobert

1
或者,find ./dir -mindepth 2 -type f -exec mv -f '{}' ./dir ';'如果覆盖重复项
Atav32

33

在zsh中:

mv dir1/*/**/*(.D) dir1

**/递归遍历子目录。该水珠预选赛 .只有符合规则的文件,并D确保点文件包含(默认情况下,文件的名字开始与一个.被排除通配符匹配)。在事后清理目前空目录,运行rmdir dir1/**/*(/Dod)- /限制了目录,od第一批订单的深度匹配,从而去除dir1/dir2/dir3之前dir1/dir2

如果文件名的总长度很大,则可能会遇到命令行长度的限制。Zsh具有内置插件,mv并且rmdir不受此限制的影响:运行zmodload zsh/files以启用它们。

仅使用POSIX工具:

find dir1 -type f -exec mv {} dir1 \;
find dir1 -depth -exec rmdir {} \;

或(速度更快,因为它不必为每个文件运行单独的进程)

find dir1 -type f -exec sh -c 'mv "$@" dir1' _ {} +
find dir1 -depth -exec rmdir {} +

1
这应该是公认的答案!尤其是简洁的zsh版本。
亚当斯基


2

我写了两个可以一起使用的函数,它们可以通过添加-maxdepth $VAL参数来限制目录级别。

# This scripts flattens the file directory
# Run this script with a folder as parameter:
# $ path/to/script path/to/folder

#!/bin/bash

rmEmptyDirs(){
    local DIR="$1"
    for dir in "$DIR"/*/
    do
        [ -d "${dir}" ] || continue # if not a directory, skip
        dir=${dir%*/}
        if [ "$(ls -A "$dir")" ]; then
            rmEmptyDirs "$dir"
        else
            rmdir "$dir"
        fi
    done
    if [ "$(ls -A "$DIR")" ]; then
        rmEmptyDirs "$DIR"
    fi
}

flattenDir(){
    local DIR="$1"
    find "$DIR" -mindepth 2 -type f -exec mv -i '{}' "$DIR" ';'
}

read -p "Do you wish to flatten folder: ${1}? " -n 1 -r
echo    # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
    flattenDir "$1" &
    rmEmptyDirs "$1" &
    echo "Done";
fi

伙计,我只是忘记了path参数而误用了您的脚本,那真是搞砸了我的服务器。好的,我是复制粘贴内容并滥用它们的人,但是,伙计们,请明智地在脚本上添加检查/确认,以删除/移动类似的东西……
dulgan

哎呀!我很遗憾听到。希望您有备份...我已添加确认以供将来保护。
布鲁诺

谢谢@Bruno,这样更好。我的服务器仍在正常运行,我评论了“展平”部分,只是从根目录(这是我的错误)中递归删除了空目录,直到看到导致我停止运行脚本的错误为止。
dulgan

1

由于我有一个平整包含同名文件的目录的用例,因此扩展了该问题的流行答案。

dir1/
├── dir2
   └── file
└── dir3
    └── file

在这种情况下,传递给-i--interactive)选项mv将不会产生使目录结构变平和处理名称冲突的预期结果。因此,只需将其替换为--backup=t(等效于--backup=numbered)。有关-b--backup)选项的更多文档,请访问https://www.gnu.org/software/coreutils/manual/coreutils.html#Backup-options

导致:

find dir1/ -mindepth 2 -type f -exec mv -t dir1/ --backup=t '{}' +

产生:

dir1/
├── dir2
├── dir3
├── file
└── file.~1~

1

tar和zip都具有合并然后剥离目录结构的功能,因此我能够使用以下命令快速平整嵌套目录

tar -cvf all.tar *

然后将all.tar移到新位置,然后

tar -xvf all.tar --strip=4

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.