我有一堆包含空格的文件和目录(每个文件名中有几个),我正在尝试删除它们。我陷入了rename
不理解正则表达式的“愚蠢”之中。到目前为止我最终得到了类似的东西
find . -maxdepth 1 -type d -print0 | perl -pe 'print $_; s/\s+/_/g' | xargs -n 2 -0 mv
find
尝试结合(目前我只想重命名第一级目录;我试图规避xargs
使用空格作为分隔符的问题-print0
)和 Perl 正则表达式的力量。
它正在朝着正确的方向发展(如果我最终改变,mv
我echo
会得到我想要的),但它并不能完全发挥作用。
任何通用建议将不胜感激!
答案1
由于您perl
已经使用来生成新名称,因此只需执行rename
in perl
:
find . -maxdepth 1 -type d -print0 |
perl -0nE '$o=$_; s/\s+/_/g; rename $o, $_'
您还可以使用新奇的方法/r
并执行以下操作:
perl -0nE 'rename $_, s/\s+/_/gr or die "$_: $!\n"'
答案2
答案3
既然您无论如何都在使用 Perl,为什么不在 Perl 中完成所有操作呢?
perl -w -Mstrict -MFile::Find -E 'finddepth(sub { rename $_, s/\s+/_/gr }, ".")'
(-w
和-Mstrict
选项不是必需的,但它们是很好的实践,因为它们启用警告和严格模式。)
请注意,上面的代码很乐意将文件重命名为已存在的名称,并删除该名称的先前文件。为了避免这种情况,你可以这样做:
perl -w -Mstrict -MFile::Find -E 'finddepth(sub { my $n = s/\s+/_/gr; rename $_, $n unless -e $n }, ".")'
此外,任何来自 的错误都rename
将被默默地忽略。这也不是特别难修复,但是对于一行代码来说,代码有点冗长。不过,您始终可以编写实际的 Perl 脚本,例如:
#!/usr/bin/perl
use warnings;
use strict;
use File::Find qw(finddepth);
use Errno qw(EEXIST);
sub safe_rename {
my($old, $new) = @_;
$! = EEXIST and return if -e $new;
rename $old, $new;
}
finddepth(sub {
my $new = s/\s+/_/gr;
return if $_ eq $new;
safe_rename $_, $new or warn "Error renaming $_ to $new in $File::Find::dir: $!\n";
}, @ARGV);
如果将此脚本保存为(例如)rename_space.pl
,则可以运行它perl rename_space.pl
(或者只是./rename_space.pl
使其chmod
可执行)并将要处理的目录(例如.
)作为命令行参数传递。
答案4
与从 角度思考相比find | perl | xargs
,循环遍历每个文件通常更容易、更干净、更安全。已经重命名的目录是这里的一个陷阱,这就是循环体不止一行的原因。这可以用任何语言完成,但这里是纯 bash 中的一个(除了命令mv
):
#!/usr/bin/env bash
set -eu
shopt -s nullglob globstar
for f in ./**/*' '*; do
directory="${f%/*}"
filename="${f##*/}"
# The direcory would be renamed in a previous iteration
# (because bash expands globs alphabetically).
src="${directory// /_}/$filename"
dst="${f// /_}"
echo mv --no-clobber --no-target-directory -- "$src" "$dst"
done
提示:echo
一旦您对它的作用感到满意,请删除该前缀。
该脚本仅替换空格。要替换其他空白,请进行相应调整。正如所评论的,这可以通过 来增强extglob
,但我就到此为止。