在声明它重复之前,请考虑我出于特定原因需要它:批量重命名(或复制到新名称)包含文件和目录名称中的公共字符串的树结构。这是一个示例(在 Ubuntu 14.04 上尝试过,因此使用 GNU 工具):
cd /tmp
mkdir myproj
mkdir -p myproj/myproj_AA/myproj_BB
touch myproj/myproj_AA/myproj_BB/myproj_CC.dat
mkdir myproj/myproj_AA/myproj_DD
touch myproj/myproj_AA/myproj_DD/myproj_EE.dat
mkdir -p myproj/myproj_XX/myproj_YY
touch myproj/myproj_XX/myproj_YY/myproj_ZZ.dat
mkdir -p myproj/myproj_XX/myproj_WW
touch myproj/myproj_XX/myproj_WW/myproj_QQ.dat
tree myproj # to visualise
这个目录结构tree
看起来像这样:
myproj
├── myproj_AA
│ ├── myproj_BB
│ │ └── myproj_CC.dat
│ └── myproj_DD
│ └── myproj_EE.dat
└── myproj_XX
├── myproj_WW
│ └── myproj_QQ.dat
└── myproj_YY
└── myproj_ZZ.dat
6 directories, 4 files
所以,我希望 中的所有条目myproj/
,包括myproj
它本身,都重命名为myTESTproj
而不是myproj
(无论它可能作为名称出现在哪里)。所以,首先我需要获得一个相对于当前目录的相对路径列表 - 然后我需要对其进行排序,以便最外面的孩子(我认为这相当于具有最长相对路径名的文件,但不确定)是第一个(因为如果我先重命名/mv 目录,然后尝试重命名其中的文件,它可能会使用旧的目录名称作为第一个参数,并且由于名称现在已更改而失败)。
我知道首先ls -R --group-directories-first myproj/
要使用ls
递归和组目录,但它的输出是这样的:
$ ls -R --group-directories-first myproj/
myproj/:
myproj_AA myproj_XX
myproj/myproj_AA:
myproj_BB myproj_DD
myproj/myproj_AA/myproj_BB:
myproj_CC.dat
myproj/myproj_AA/myproj_DD:
myproj_EE.dat
myproj/myproj_XX:
myproj_WW myproj_YY
myproj/myproj_XX/myproj_WW:
myproj_QQ.dat
myproj/myproj_XX/myproj_YY:
myproj_ZZ.dat
...也就是说,它不是带有子路径的简单列表,我可以轻松地提供给while read f; do ...
我最接近的是使用find
:
$ find myproj/
myproj/
myproj/myproj_AA
myproj/myproj_AA/myproj_DD
myproj/myproj_AA/myproj_DD/myproj_EE.dat
myproj/myproj_AA/myproj_BB
myproj/myproj_AA/myproj_BB/myproj_CC.dat
myproj/myproj_XX
myproj/myproj_XX/myproj_YY
myproj/myproj_XX/myproj_YY/myproj_ZZ.dat
myproj/myproj_XX/myproj_WW
myproj/myproj_XX/myproj_WW/myproj_QQ.dat
所以,在这里我确实有一个简单的子路径列表,但是它首先向叶节点排序根节点 - 我首先需要叶节点。我正在尝试类似的东西find myproj/ | sort -n
,但似乎没有什么区别。因此,如果我执行以下操作:
$ find myproj/ | sort -n | while read f; do mv -v $f $(echo $f | sed 's/myproj/myTESTproj/g'); done
‘myproj/’ -> ‘myTESTproj/’
mv: cannot stat ‘myproj/myproj_AA’: No such file or directory
mv: cannot stat ‘myproj/myproj_AA/myproj_BB’: No such file or directory
mv: cannot stat ‘myproj/myproj_AA/myproj_BB/myproj_CC.dat’: No such file or directory
...
...然后预期的递归重命名立即失败,因为根节点(目录)首先被重命名,因此对它的所有进一步引用都是无效的。
那么,如何首先获得带有叶节点的子目录的正确递归列表,以便像这样在批量重命名中使用它?
如果您的目标只是重命名,那么在目录本身之前处理每个目录的内容还不够吗,也就是说,您不需要首先(来自所有目录)的所有叶子?正是这样做的。
find -depth
然后您可以使用
find -exec
和 Bash 重命名文件:如果您安装了 Perl 版本的
rename
命令(有时称为prename
),这将适用于您确保任何目录中的子项列在目录本身之前的选项
-depth
。find
操作的+
后缀允许对指定命令的一次调用-exec
进行多次插入。{}
以降低效率为代价,您可以将其替换为\;
.当你确定它会做你想做的事时,删除
-n
或替换它-v
。我记得发布问题后要查找的内容 -如果叶节点是具有最长相对路径名的节点(我不确定它是否总是如此,但似乎至少在 OP 示例中),那么一个简单的需要一种按字符串长度对字符串列表进行排序的方法;不幸
sort
的是似乎没有这样的选择。但是,我找到了https://stackoverflow.com/questions/5917576/sort-a-text-file-by-line-length-include-spaces - 并从那里选择了
perl
解决方案:但是,微不足道的
sed 's/myproj/myTESTproj/g'
替换在这里也不起作用:...所以我们
sed
只需要替换一行中的最后一个匹配项,即sed -E 's/(.*)myproj/\1myTESTproj/g'
:我想这是我想要的 - 但是,我不确定最长路径名 == 叶文件节点的假设是否总是正确的;即使是这样 - 有没有更简单的方法可以做到这一点?
编辑:在这样的结构情况下,这肯定会失败:
...也就是说,如果要在重命名的路径中搜索和替换的子字符串的第一次出现也是最后一次(唯一的);它出现在列表中多次出现子字符串的路径之前。