我有一个文件夹结构:
/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
不为空,您仍然可以这样做,但不是rmdir
,mv /mnt/data/mydata.old/* /mnt/data/mydata/
。如果它有几个条目,而子目录有几百万个条目,那么这将节省一些 I/O。
您确实需要 的写权限/mnt/data
,而另一种方式则不需要。但是这不需要 的写权限/mnt/data/mydata/mydata
。
按照您喜欢的方式编写脚本,和/或cd
和使用相对路径。选择您想要的任何临时名称,如foo
或tmp
,或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/mydata
readline 行编辑来交互地编辑路径(与 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