我的 Ubuntu 22.04 系统上的 ~home 下(在许多子目录中)有一些文件。我相信大多数带空格的文件名最初来自 Windows。
我使用以下find命令查找有问题的文件
查找 .-name "* *"
有什么方法可以将名称更改为在名称中当前包含空格的所有文件中包含 _ 或其他字符?
例如我想改变
MSC 机脚订单.pdf
到
MSC_机器_脚顺序.pdf
使用 find 命令,我确定有大约 600 个文件需要更改其名称,因此我希望采用某种自动化的方式来更改名称。
当我运行某些脚本时,带有空格的文件名会导致问题。
提前感谢您的帮助。
您可以使用参数扩展将 或空格替换为下划线:
${1// /_}
意思是“用下划线替换第一个参数中的每个空格(请参阅中的参数扩展man bash
)。bash -c
从第一个非选项参数运行命令。此处的命令是mv
用下划线替换空格的命令。bash -c
现在并不重要,因此我使用了--
。其余参数设置为命令的位置参数。find -exec
对找到的每个文件执行命令。{}
被文件名替换,最后的\;
表示find
命令的结束位置。因此,find 调用bash -c
并发送文件名作为第一个参数,因此可以重命名。需要这个复杂的咒语来保留第一个参数中的空格
mv
。如果您也想重命名子目录,则需要使用
-depth
and-execdir
而不是-exec
(否则,目录名称会发生变化,并且 find 无法再找到它来处理其内容)。请注意,更改文件的名称也可能会导致问题 - 如果程序期望存在具有给定名称的文件,则更改该名称可能会导致程序失败。
perl-rename aka
rename
akaprename
非常适合模式替换文件重命名。请参阅https://francopasut.netlify.app/post/linux-rename-confusion/ re:您可以找到此命令的各种名称,并且 util-linux 还曾经发布过rename
不使用正则表达式的不同命令。快速浏览后,我认为 Ubuntu 现在将其作为 提供rename
。从旧的手册页(来自 Trusty)来看,当时它是perl
软件包的一部分;如果情况仍然如此,则您已经安装了它。它需要一个 perl 语句来应用于其命令行上的每个文件名。该
s/regex/replacement/
运算符隐式地对(当前行,在本例中为当前文件名)进行操作,因此非常有用。(如果您想在路径中匹配和替换,$_
可以使用模式分隔符。)有关 perl 运算符的详细信息,请参阅https://linux.die.net/man/1/perlop。s{pat}{rep}
/
s//
对于您的情况,只需
prename 's/ /_/g'
一次对任意数量的文件运行即可。与 sed 类似,/g
修饰符会替换匹配的每一个出现位置。使用find -execdir
可确保仅看到文件名,而不是目录名,因为无论是否已重命名prename
,尝试移动foo bar/x y.txt
到都会失败(源目录或目标目录都不存在)。foo_bar/x_y.txt
foo bar/
-i
在覆盖文件之前提示。如果您不打算覆盖任何内容,这通常是一个好主意。它还有一个-n
aka--dry-run
选项。我还没有检查
find
手册页,了解-depth
在递归过程中重命名目录是否有必要。在这种情况下,这样做不会有什么坏处,所以我把它包括进去了。choroba 的答案是 fork+exec
bash
,mv
对于每个要重命名的文件。prename
只会进行一点文本处理和rename(2)
系统调用。更重要的是,对于一次性交互使用,它是一个更简单的命令行。公平地说,我确实不得不
-execdir
在目录包含空格的情况下使用它,这是我在写这个答案的过程中才想到的。我不确定-depth
我们的答案是否有必要;我认为无论哪种方式都适用。在一个更简单的情况下,所有文件都在一个目录中,您只需
prename -i 's/ /_/g' *\ *
使用一个将文件名与空格匹配的 glob 表达式即可。或者启用 Bash
set -o extglob
,prename ... **/*\ *
进行递归匹配。但这会让 prename 看到目录名称;如果它们包含空格,它将无法工作,除非您使 perl 代码更复杂,例如分离出基本名称,修改它,然后将其重新添加。或者您可以运行它几次,直到您不再收到错误,因为目录会重命名,因此下一个深度级别将成功。如果您切换到 zsh,那么您可以使用贡献的 zmv 模块:
(
-n
仅用于测试 - 当您确信所建议的重命名操作正确时,请将其删除)。(“globstar”)运算
**
符使模式匹配像find
命令一样递归下降到子目录中。前两组括号将目录和文件名捕获到单独的编号变量中,这些变量可用于替换字符串,而第三组(#q.D)
是glob 限定符,其中.
将匹配限制为纯文本文件(如find
命令的-type f
)D
包括(隐藏)“点文件”(为了与您原来的find
表达保持一致 - 如果您不需要匹配此类文件,则可以省略它)。替换
$1${2// /_}
将未修改的目录部分与修改后的文件名连接起来 -'$1${2:gs/ /_}'
如果您愿意,您也可以使用历史样式的扩展来编写。参见man zshcontrib和man zshexpn。