将文件从第二级子目录“向上移动一级”

将文件从第二级子目录“向上移动一级”

基本上,给定一个父目录 (PARENT),包含 1..M 个目录 (DIRA-DIRZ),每个目录包含 0..N 个目录 (DIR1-DIRN),每个目录包含一个文件列表 (FILEa-FILEz)。 。

PARENT/DIRA/DIR1/files
PARENT/DIRB/files
PARENT/DIRC/DIR2/files
PARENT/DIRC/DIR3/files

...如果二级目录(DIR1-DIRN)存在,则将文件从二级目录(DIR1)移动到包含它的一级目录(DIRA),然后删除现在空的二级目录(DIR1) )。

PARENT/DIRA/files
PARENT/DIRB/files
PARENT/DIRC/files

我发现我正在尝试使用这个片段:

find /thisdir -type f -name "*.ogg" -exec mv {} /somedir \;

但我无法弄清楚外循环以及如何集成它们。

您能提供的任何帮助,尤其是一些解释,都将非常出色!

答案1

由于几乎所有 Linux 发行版(至少是主要发行版)都预装了 Python,因此编写一个简单的 Python 脚本可能会更容易。

#!/usr/bin/env python3
import sys
from pathlib import Path


def flatten_and_del(targdir):
    pardir = targdir.parent
    for f in targdir.glob("*"):
        f.rename(pardir / f.name)
    targdir.rmdir()


def process_dir(parent):
    parent = Path(parent)
    if not parent.is_dir():
        raise RuntimeError(f"{parent} is not a directory!")
    for f in parent.glob("*"):
        if not f.is_dir():
            continue
        flatten_and_del(f)


if __name__ == "__main__":
    process_dir(sys.argv[1])

将其另存为,例如,tidy-dirs.py并像这样运行:

python3 tidy-dirs.py path/to/PARENT

警告:正如 αГsнιη 在他们的评论中提到的,这种方法将要如果文件名冲突会导致数据丢失。如果您不希望发生这种情况,请在执行之前flatten_and_del添加target.exists()检查.rename()

答案2

zsh

autoload zmv # best in ~/.zshrc
zmv '(PARENT)/(*)/*/(*)(#qD)' '$1/$2/$3'
rmdir PARENT/*/*(#qD/^F)

答案3

实际上这是两个命令。第一个是mv上一级的文件,第二个是删除空目录。
(这两个命令都连接在一起,&&使其看起来像一行)

find -H PARENT -mindepth 3 ! -type d -execdir cp --backup=numbered -al {} .. \; -delete && find -H PARENT -depth -mindepth 2 -type d -exec rmdir {} +

而不是mv创建硬链接并删除源。cp提供内置备份解决方案:

cp--backup=numbered将自动重命名文件,以防另一个文件已存在
cp-l创建硬链接而不是复制
cp-a保留元数据(mtime、权限)
find-delete递归删除所有内容(谨慎使用)

所有find论点均已解释:

-H如果 PARENT 是符号链接,则查找
查找与(= 反转参数)! -type d相同只 查找第 3 级的 */*/*/ 文件(用于使其更安全)从子目录 查找运行命令-type f!
-mindepth 3-maxdepth 3
-execdir

\;和之间的区别+解释:

找到-exec CMD {} \;CMD ARG1的串行1:1执行;命令 ARG2; ... (其中 ARG 是 find 的结果)
find-exec CMD {} +并行执行 CMD ARG1 ARG2 ARG3 ... ARGN;


PARENT您可以替换"$@"并创建一个小 shell 脚本,该脚本将在多个参数上运行,而不是从终端运行。

sh ./myscript.sh PARENT [PARENT2...]运行脚本

#!/bin/sh
test -e "$1" || exit 1
find -H "$@" -mindepth 3 ! -type d -execdir cp --backup=numbered -al {} .. \; -delete
find -H "$@" -depth -mindepth 2 -type d -exec rmdir {} +

答案4

如果您有很多目录需要执行此操作,则可以使用简单的 bash 脚本轻松解决此问题,以节省时间。

只有一个目录:

parent/
└── DIRA
    └── DIR1
        ├── file1.ogg
        ├── file2.ogg
        └── file3.ogg

然后,在 DIR1 内部:

# You can also use ; over && but the second one is more reliable,
# specially if you are doing this in a remote server.

mv *.ogg ../ && cd ../ && rmdir DIR1/

结果:

parent/
└── DIRA
    ├── file1.ogg
    ├── file2.ogg
    └── file3.ogg

如果您确定二级目录中的所有文件都是.ogg,则可以使用MV *代替MV *.ogg

相关内容