![使用单个 bash 命令批量重命名文件夹](https://linux22.com/image/97278/%E4%BD%BF%E7%94%A8%E5%8D%95%E4%B8%AA%20bash%20%E5%91%BD%E4%BB%A4%E6%89%B9%E9%87%8F%E9%87%8D%E5%91%BD%E5%90%8D%E6%96%87%E4%BB%B6%E5%A4%B9.png)
我有这样的文件夹设置:
/path/to/directory/SLUG_1/SLUG_1 - SLUG_2 - SLUG_3
SLUG_2 是年份,年份后面可能有字母,如“1994”或“2003a”。
我想将这些文件重命名为:
/path/to/directory/SLUG_1/SLUG_2 - SLUG_3
我已经非常接近这个命令了:
find $root -mindepth 2 -maxdeth 2 -type d | sed "s#\(\(/path/to/directory/[^/]*/\).* - \([0-9]\{4\}[a-bA-B]\? - .*\)\)#mv "\1" "\2\3"#
这打印:
mv "/path/to/directory/SLUG_1/SLUG_1 - SLUG_2 - SLUG_3" "/path/to/directory/SLUG_1/SLUG_2 - SLUG_3"
这正是我想要执行的命令。但我无法执行它。
我尝试将输出分配给变量并通过调用该变量来执行它。那行不通。我尝试了这个想法的一些变体,但出现了错误。
感觉就像我在这里缺少一些工具。一些迭代工具可以使这项工作变得容易。有什么建议么?
答案1
假设所有子目录都/path/to/directory
遵循该命名约定:
cd /path/to/directory
prename -n 's~/\d{4}[a-z]? - ~/~i' */*
prename
是个Perl重命名(任何变体都可以)。
答案2
这打印:
mv ...
这正是我想要执行的命令。但我无法执行它。
我尝试将输出分配给变量并通过调用该变量来执行它。那行不通。我尝试了这个想法的一些变体,但出现了错误。
感觉就像我在这里缺少一些工具。
您正在寻找的简单方法是附加| bash
到您的命令中。这就是如何从“打印命令的命令”到“实际运行打印的命令”。
但是,即使您在要打印的命令中包含双引号,这是一个馊主意包含在脚本中。
您可能已经处理了空格方面的问题,但是如果文件名直接包含双引号字符怎么办?还是换行符?或者变量名(将在双引号内展开)?
这仅有的文件名中非法的字符是斜杠 ( /
) 和空字节。
在编写脚本时,您应该保持比交互式命令行更高的稳健性标准。
在命令行中,您可以看到要运行的命令,可以确认它是否正确,然后可以运行它。在剧本中,没有监督,也没有确认。该脚本将执行您告诉它执行的操作,无论这可能会产生多大的破坏性。
所以正确的方法实际上是使用find
,详见我的其他答案。这将处理任何文件名正确并且不包含任意代码执行漏洞。
答案3
另一个答案是使用 GNUfind
来处理重命名。无论文件名中包含哪些字符,此方法都很可靠。
如果我正确理解您的用例,您想要重命名以其父目录的全名开头的目录。换句话说,如果您的目录是这样命名的:
/some/path/abcdefghi/abcdefghi - something - else/
你想像这样重命名它:
/some/path/abcdefghi/something - else/
由于您在这个问题上指定了 GNU 作为标签,因此您可以使用该find
命令的 GNU 扩展并按如下方式处理:
find . -mindepth 2 -maxdepth 2 -type d -regextype posix-egrep -regex '.*/([^/]+)/\1[^/]+' -exec sh -c 'new="$(sed -r "s:/([^/]+)/\\1 ?-? ?([^/]+)\$:/\\1/\\2:" <<<$1)"; mv "$1" "$new"' find-sh {} \;
检测结果:
[vagrant@localhost test]$ mkdir -p SLUG_1/SLUG_{1\ -\ SLUG_{2..4}\ -\ SLUG_5,7\ -\ something}
[vagrant@localhost test]$ find . -type d
.
./SLUG_1
./SLUG_1/SLUG_1 - SLUG_2 - SLUG_5
./SLUG_1/SLUG_1 - SLUG_4 - SLUG_5
./SLUG_1/SLUG_1 - SLUG_3 - SLUG_5
./SLUG_1/SLUG_7 - something
[vagrant@localhost test]$ find . -mindepth 2 -maxdepth 2 -type d -regextype posix-egrep -regex '.*/([^/]+)/\1[^/]+' -exec sh -c 'new="$(sed -r "s:/([^/]+)/\\1 ?-? ?([^/]+)\$:/\\1/\\2:" <<<$1)"; mv "$1" "$new"' find-sh {} \;
[vagrant@localhost test]$ find . -type d
.
./SLUG_1
./SLUG_1/SLUG_3 - SLUG_5
./SLUG_1/SLUG_4 - SLUG_5
./SLUG_1/SLUG_2 - SLUG_5
./SLUG_1/SLUG_7 - something
[vagrant@localhost test]$