我有一个脚本,可以在文件名中搜索空格字符“ ”、感叹号“
!
”和美元符号“ $
”,并将每个字符替换为下划线“ _
”。但是,它根本不处理带有感叹号的文件名。在当前文件夹和所有子文件夹中搜索要重命名的文件,直到一定深度(此处不太相关)。文件夹名称也应该以同样的方式更改,这就是深度交错的原因。
这是所需的重命名方案:
This is cool!.txt --> This_is_cool_.txt
Thank$.log --> Thank_.log
Foo 1/Bar 2/a.txt --> Foo_1/Bar_2/a.txt
这是脚本:
#!/bin/tcsh -f
foreach n ( 1 2 3 4 5 6 7 8 )
find . -mindepth $n -maxdepth $n -name '*[ $\!]*' | fgrep -v \" | \
awk '{printf "mv -i -- \"%s\" \"%s\"\n", gensub("!","\\\\\!","g",$0), gensub(" ","_","g",gensub("\\$","\\\\$","g",gensub("!","\\\\\!","g",$0)))}' | tcsh -cf
end
\
我改变了两个感叹号之前的反斜杠 ' ' 的数量,但没有效果。错误消息可能如下所示(偶数个反斜杠):
awk: cmd. line:1: warning: escape sequence `\!' treated as plain `!'
或者 shell 脚本(显然)打开一个交互式 shell?!有时什么也没有发生(我想当第一个参数中有太多反斜杠mv
并且找不到文件时)。
PS:正如你所看到的,我排除了带有双引号“ "
”的文件,因为我也无法让它们工作。如果您也能建议如何处理这些问题,那就更好了!
答案1
mv
直接调用比合成命令awk
并将其通过管道传输到 shell更容易
#!/bin/bash
#
find -mindepth 1 -maxdepth 8 -type f -name '*[ !$]*' -execdir
bash -c 'for f in "$@"; do echo mv -- "$f" "${f//[ !$]/_}"; done' _ {} +
此版本重命名文件,但不重命名目录(如问题中所述)。如果您实际上也想重命名目录,请使用此变体,
find -mindepth 1 -maxdepth 8 -depth -name '*[ !$]*' -execdir
bash -c 'for f in "$@"; do echo mv -- "$f" "${f//[ !$]/_}"; done' _ {} +
在这两种情况下,echo
当您满意该命令将执行您所期望的操作时,请删除它。用于mv -i
交互式重命名。中的下划线bash -c '...' _ {}
成为在子 shell 中运行的代码的“名称”。它被映射到$0
并将用于报告代码错误(如果有),因此您可以放在bash
那里,甚至subshell
- 它只是一个标签。
答案2
此脚本假设您不想限制 的深度find
并且希望将所有字符替换$!"'
为_
。
由于问题不包含有关操作系统或工具的特定版本的信息,我尝试仅依赖 POSIX 规范,而不需要像 GNU 这样的特定版本。
#!/bin/sh
find . -depth -name '*[ $!"]*' | while IFS= read -r file
do
dir="${file%/*}"
newname="$(echo "${file##*/}" | tr ' $!"' '____')"
echo mv "$file" "$dir/$newname"
done
该脚本仅经过轻微测试。如果它打印正确的mv
命令,请删除echo
.
解释:
find . -depth
...确保文件在目录之前打印(如果两者都包含要替换的字符)。这样我们将首先重命名文件,然后重命名父目录。
父目录名称以“`分隔dir="${file%/*}
,保持不变。
文件名(或最后一个目录名)被提取并更改为newname="$(echo "${file##*/}" | tr ' $!"' '____')"
.
进一步改进
要处理文件名中的其他“奇怪”字符,最好直接从 的操作中调用循环体中的代码,find
如下-exec
所示罗埃马的回答。
find . -depth -name '*[ $!"]*' -exec sh -c 'for file in "$@" ; do dir="${file%/*}" ; newname="$(echo "${file##*/}" | tr '\'' $!"'\'' "____")" ; echo mv "$file" "$dir/$newname"; done' _ {} +
也可以看看https://mywiki.wooledge.org/BashFAQ/020
在另一个答案中使用-execdir
as 可以避免拆分文件名和父目录,但 POSIX 未定义此操作。
可以用诸如 之类的外壳替换......echo
组合。|tr
"${newname//[ $!\"]/_}"