如何将所有文件名中的空格替换为下划线?

如何将所有文件名中的空格替换为下划线?

下面给出的脚本将“下划线”替换为特定文件夹中所有文件名中的“空格”。我无法编写一个 shell 脚本,该脚本将“下划线”替换为所有子文件夹及其所含文件的名称中的“空格”,而不仅仅是文件夹中的“空格”。

有人对我如何做到这一点有什么建议吗?

这是我的代码:

#!/bin/bash
ls | while read -r FILE; do
  mv -v "$FILE" `echo $FILE | tr ' ' '_'`
done

答案1

使用 find 在目录和子目录中搜索:

while IFS='' read -r -d '' fname ; do
   nname="${fname##*/}"
   mv -v -n "${fname}"  "${fname%/*}/${nname//[[:space:]]/_}"
done < <(find "$(pwd)"  -name "* *" -type f  -print0)

find "$(pwd)" -type f -print0- 打印当前目录和子目录中找到的所有文件路径。

随着过程替换的进行,find 命令的输出被发送到 while 循环,在那里读取变量 fname。

nname="${fname##*/}"- 从路径中提取文件名

"${fname%/*}"- 提取路径

"${nname//[[:space:]]/"_"}"- 将文件名中的空格替换为 _

"${fname%/*}/${nname//[[:space:]]/"_"}"- 路径/新文件名

答案2

使用rename公用事业例如

rename "s/ /_/g" *

由 Perl 提供支持的文件重命名脚本,具有许多有用的内置功能。


对于递归重命名,请尝试:

rename "s/ /_/g" **/*.*

其中**Bash 通配符选项(通过 启用shopt -s globstar)。

或者使用find,例如

find . -type f -execdir rename "s/ /_/g" {} ';'

答案3

bash(不确定衍生品和sh!)中,您可以使用变量替换变量等

FILE="file name"
touch "$FILE"
mv -v "$FILE" "${FILE// /_/}"

但是这至少不处理其他空格字符(制表符等)

答案4

我的方法:

find . -depth -name "* *" -execdir bash -c 'pwd; for f in "$@"; do mv -nv "$f" "${f// /_}"; done' dummy {} +

为了便于阅读,使用多行版本:

find . -depth -name "* *" -execdir \
   bash -c '
      pwd
      for f in "$@"; do
          mv -nv "$f" "${f// /_}"
      done
   ' dummy {} +

解释:

  • find . -name "* *"查找需要重命名的对象。注意,find它的测试非常灵活,因此如果您只想重命名目录,请从 开始find . -depth -type d -name "* *"
  • -execdirbash在对象所在的目录中执行给定的进程 ( ),因此传递的任何路径{}始终像./bar,而不是./foo/bar。这意味着我们不需要关心整个路径。缺点是mv -v不会向您显示路径,所以我pwd只是为了信息而添加的(如果您愿意,可以省略它)。
  • bash让我们使用"${baz// /_}"语法。
  • -depth确保不会发生以下情况:find重命名目录(如果适用),然后尝试通过其老的小路。
  • {} +能够bash喂养多种的对象(与语法相反{} \;)。我们使用 来迭代它们for f in "$@"。重点是不要bash为每个对象运行单独的进程,因为创建新进程的成本很高。我认为我们无法轻松避免运行单独的mv-s;不过,减少调用次数bash似乎是一个很好的优化(pwd是内置的bash,不会花费我们一个进程)。但是-execdir ... {} +不会将来自不同目录的文件一起传递。通过使用-exec ... {} +而不是 ,-execdir ... {} +我们可以进一步减少进程数,但我们需要关心路径,而不仅仅是文件名(比较另一个答案,看起来效果不错,但是while read减慢速度)。这是速度与(相对)简单性的问题。我的解决方案-exec如下。
  • dummy就在 之前{}成为$0我们的 内部bash。我们需要这个虚拟参数,因为"$@"相当于"$1" "$2" ...(非"$0" "$1" ...)。这样,传递的所有内容{}稍后都可以用作"$@"

更复杂,略微优化的版本(各种${...}技巧取自另一个答案):

find . -depth -name "* *" -exec \
   bash -c '
      for f in "$@"; do
          n="${f##*/}"
          mv -nv "$f" "${f%/*}/${n// /_}"
      done
   ' dummy {} +

另一种(实验性的!)方法涉及vidir. 诀窍是vidir使用$EDITOR哪个可能不是一个交互式编辑器

find . -name "* *" | EDITOR='sed -i s/\d32/_/g' vidir -

注意事项:

  • 对于包含特殊字符(例如换行符)的文件/目录名称,此操作将失败。
  • 我们不能s/ /_/g直接使用,\d32这是一种解决方法。
  • 由于vidir工作方式的原因,如果您想替换数字或制表符,方法就会变得棘手。
  • 这里vidir涉及路径,而不仅仅是文件名(基本名称),因此仅重命名文件(即不是目录)可能很困难。

不过,如果你知道自己在做什么,那么这可能会更快地完成工作。不过,我不建议vidir在一般情况下这样使用。我把它包括在我的答案中,因为我发现这种实验方法很有趣。

相关内容