将重复的文件夹名称上移一级

将重复的文件夹名称上移一级

我有一个文件夹结构:

/mnt/data/mydata/mydata/several-files-and-folders-here

我希望它是:

/mnt/data/mydata/several-files-and-folders-here

我试过了,mv但它抱怨文件夹不是空的,我找不到我能理解的可行快速解决方案,我刚开始学习 Linux。如何正确修复这个问题?

答案1

将子目录下的所有文件移动到父目录中是一种方法,但如果有巨大的中的条目数/mnt/data/mydata/mydata/,并且这是中的唯一条目/mnt/data/mydata,您可以考虑这 3 个元数据操作序列,这些操作不会更改大目录的内容,只是将其移动到文件系统中的其他位置。并从小目录中删除 1 个条目,然后取消链接。

因此它不会影响子目录中任何文件的 ctime,甚至不必列出该大目录的内容。

 mv  /mnt/data/mydata            /mnt/data/mydata.old   # rename parent
 mv  /mnt/data/mydata.old/mydata /mnt/data/mydata   # subdir -> sibling of parent
 rmdir  /mnt/data/mydata.old     # fails if not empty

目录 inode 最初为,/mnt/data/mydata/mydata现在为/mnt/data/mydata,其内容未发生改变。如果它包含数百万个文件,则速度更快,并且所需的 I/O 更少。但需要输入更多内容,因为mv不会为您创建临时名称。

如果原始目录/mnt/data/mydata不为空,您仍然可以这样做,但不是rmdirmv /mnt/data/mydata.old/* /mnt/data/mydata/。如果它有几个条目,而子目录有几百万个条目,那么这将节省一些 I/O。

您确实需要 的写权限/mnt/data,而另一种方式则不需要。但是这不需要 的写权限/mnt/data/mydata/mydata

按照您喜欢的方式编写脚本,和/或cd和使用相对路径。选择您想要的任何临时名称,如footmp,或xyz123。(如果其他用户可以写入/mnt/data,并且您正在编写脚本,请检查mv如果他们使用您选择的相同随机名称创建文件会发生什么;或者使用mktemp -p /mnt/data/在那里获取临时目录以mydata在您取消链接时将子级移入其中/mnt/data/mydata。)

它的缺点是,在操作之间会有一段时间不/mnt/data/mydata存在任何内容。除非有一个 shell 包装器renameat2系统调用RENAME_EXCHANGE原子方式交换两个路径。(Linux 内核 3.15,glibc 2.28,所以它已经存在了一段时间,但rename(1)没有mv(1)使用它的选项。)

如果您正在运行服务器,这可能对您很重要,否则您可能不会关心。


以交互方式,您还可以使用imv /mnt/data/mydatareadline 行编辑来交互地编辑路径(与 bash 用于命令行编辑的方式相同。)

答案2

如何实现

完成的方式是您需要移动以下内容:

/mnt/data/mydata/mydata/

/mnt/data/mydata/

首先...然后,如果愿意的话,您可以删除现在为空的目录,例如:

rmdir -v /mnt/data/mydata/mydata/

rmdir是安全的...仅当目录为空时才会删除目录,否则会出现错误。

如何搬家

任何一个无需试运行) 简单地:

echo mv -nv -- /mnt/data/mydata/mydata/* /mnt/data/mydata/

注意:echo之前mv只是防止意外复制/粘贴的安全措施...您需要将其删除才能使命令起作用...这里没有试运行,所以当心

或者进行试运行)bash利用shell 参数扩展围绕任一重复的目录名称(例如,前导带有:"${parameter/mydata\//}"或尾随带有:"${parameter%mydata/*}${parameter##*/}")和一个for循环......一行代码就可以很简单了:

for f in /mnt/data/mydata/mydata/*; do echo mv -nv -- "${f}" "${f%mydata/*}${f##*/}"; done

注意:echo插入之前是mv为了安全的试运行...echo只有对输出满意时才删除,然后再次运行命令进行实际移动。

示范

$ tree /mnt
/mnt
└── data
    └── mydata
        └── mydata
            ├── dir1
            │   ├── file1
            │   ├── file2
            │   └── file3
            ├── dir2
            │   ├── file1
            │   ├── file2
            │   └── file3
            ├── dir3
            ├── file1
            ├── file2
            └── file3

6 directories, 9 files
$ for f in /mnt/data/mydata/mydata/*; do mv -nv -- "${f}" "${f%mydata/*}${f##*/}"; done
renamed '/mnt/data/mydata/mydata/dir1' -> '/mnt/data/mydata/dir1'
renamed '/mnt/data/mydata/mydata/dir2' -> '/mnt/data/mydata/dir2'
renamed '/mnt/data/mydata/mydata/dir3' -> '/mnt/data/mydata/dir3'
renamed '/mnt/data/mydata/mydata/file1' -> '/mnt/data/mydata/file1'
renamed '/mnt/data/mydata/mydata/file2' -> '/mnt/data/mydata/file2'
renamed '/mnt/data/mydata/mydata/file3' -> '/mnt/data/mydata/file3'
$ tree /mnt
/mnt
└── data
    └── mydata
        ├── dir1
        │   ├── file1
        │   ├── file2
        │   └── file3
        ├── dir2
        │   ├── file1
        │   ├── file2
        │   └── file3
        ├── dir3
        ├── file1
        ├── file2
        ├── file3
        └── mydata

6 directories, 9 files

相关内容