find . -depth -name ' *' -exec sh -c '
for pathname do
newname=${pathname##*/}
newname=${newname#"${newname%%[! ]*}"}
newpathname=${pathname%/*}/$newname
if [ -z "$newname" ] || [ -e "$newpathname" ]; then
continue
fi
mv -v "$pathname" "$newpathname"
done' sh {} +
Run Code Online (Sandbox Code Playgroud)
上面的代码查找当前目录中或当前目录下名称至少以一个空格字符开头的任何文件或目录。它以深度优先的顺序执行此操作(由于-depth)以避免重命名尚未处理其内容的目录;重命名目录会导致find以后找不到它,因为它已重命名。
为批量以空格开头的名称调用一个简短的内联 shell 脚本。该脚本迭代给定的路径名,并首先将当前路径名末尾的实际名称提取到变量中newname。它使用标准参数替换 来完成此操作,删除字符串中${pathname##*/}最后的所有内容(最长的前缀匹配),留下最终的路径名组件。这本质上是相同的/*/"$(basename "$pathname")"这与本例
然后,我们需要修剪掉 中字符串开头保证存在的空格$newname。我们首先删除除空格之外的所有内容${newname%%[! ]*}(最长的后缀匹配,即从第一个非空格字符开始),然后从字符串[! ]*的开头删除结果$newname${newname#"${newname%%[! ]*}"}。
命令中的目标路径由斜杠连接的mv目录路径和新名称 ie 组成,与 本质上相同。$pathname${pathname%/*}/$newname"$(dirname "$pathname")/$newname"
该代码检测名称冲突并默默地跳过对可能发生冲突的名称的处理。它还会跳过折叠为空字符串的名称。这就是if之前声明的mv作用。如果您想让用户注意到这些名称,请在 之前这样做continue。
在备份数据的副本上进行测试运行。
测试运行上面的代码:
$ tree -Q
"."
|-- " dir0"
| |-- " o dir1"
| | |-- " otherfile"
| | `-- "dir2"
| | |-- " dir3"
| | `-- " moar"
| `-- "file"
`-- "script"
4 directories, 4 files
Run Code Online (Sandbox Code Playgroud)
$ sh script
./ dir0/ o dir1/dir2/ moar -> ./ dir0/ o dir1/dir2/moar
./ dir0/ o dir1/dir2/ dir3 -> ./ dir0/ o dir1/dir2/dir3
./ dir0/ o dir1/ otherfile -> ./ dir0/ o dir1/otherfile
./ dir0/ o dir1 -> ./ dir0/o dir1
./ dir0 -> ./dir0
Run Code Online (Sandbox Code Playgroud)
请注意它是如何从目录层次结构的底部开始的。如果它先尝试重命名 dir0,则稍后将无法输入它来重命名其他目录和文件。
$ tree -Q
"."
|-- "dir0"
| |-- "file"
| `-- "o dir1"
| |-- "dir2"
| | |-- "dir3"
| | `-- "moar"
| `-- "otherfile"
`-- "script"
4 directories, 4 files
Run Code Online (Sandbox Code Playgroud)