下面给出的脚本将“下划线”替换为特定文件夹中所有文件名中的“空格”。我无法编写一个 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 "* *"
。-execdir
bash
在对象所在的目录中执行给定的进程 ( ),因此传递的任何路径{}
始终像./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
在一般情况下这样使用。我把它包括在我的答案中,因为我发现这种实验方法很有趣。