我在用案例。
以下命令有效。
$ mv camelCase (ccase -t Kebab camelCase)
现在我尝试使用以下方法重命名多个目录:
$ find . -type d -execdir rename 's/(.*)/$(ccase -t Kebab $1)/' '{}' \+
这不起作用,我收到此错误消息:
Can't rename ./camelCase3 1000 4 24 27 30 46 121 132 1000ccase -t Kebab ./camelCase3): No such file or directory
我能做些什么?
答案1
应该:
find . -depth ! -name . -type d -execdir sh -c '
for dir do
dir=${dir#*/} # remove the ./ prefix that some find implementations add
new_dir=$(ccase -t Kebab -- "$dir") || continue
[ "$dir" = "$new_dir" ] ||
mv -i -- "$dir" "$new_dir"
done' sh {} +
绝不将那些{}
嵌入到代码sh
/或任何语言解释器的参数bash
,这将使其成为命令注入漏洞。看是否可以安全地使用“find -exec sh -c”?
其他一些注意事项:
- 当您不能保证变量数据不会以 开头时
-
,请使用 来--
标记选项的结尾。我不知道是否ccase
支持--
,如果不支持,您可以将其作为错误报告给他们的维护人员。 - 这里不需要
bash
,你的系统sh
应该足够了 - 在 sh 中,就像在 bash 中一样,参数扩展和命令替换至少在列表上下文中必须被引用,否则它们会经历 split+glob!参见例如忘记在 bash/POSIX shell 中引用变量的安全隐患
- 特别是在执行诸如调用之类的潜在破坏性操作时
mv
,最好检查您运行的每个命令是否成功。这里我们使用|| continue
跳过ccase
失败的目录。 - 请注意,
+
不要 在可能的情况;
下传递多个目录,sh
这样可以避免每个文件调用一个 shell。但并非所有find
支持非标准-execdir
谓词的实现都会这样做。在某些 BSD 或旧版本的 GNU 中find
,+
只是执行相同的操作;
,然后循环是多余的(尽管无害)。 - 请注意,在 中
mv -- SomeDir some_dir
,如果some_dir
已经存在并且是一个目录或目录的符号链接,则它与 相同mv -- SomeDir some_dir/SomeDir
。通过 GNU 实现mv
,可以使用-T
选项来避免这种情况(或使用zmv
下面的方法来检测冲突)
为了避免这种情况-execdir
并运行尽可能少的 shell,您可以使用-exec
它,但随后您需要将基本名称和父目录分开。作为改进,我们还可以记录 的失败,ccase
以便mv
将其反映在find
的退出状态中:
find . -depth ! -name . -type d -exec sh -c '
ret=0
for dir do
base=${base##*/}
parent=${dir%/*}
new_base=$(ccase -t Kebab -- "$base") && {
[ "$base" = "$new_base" ] ||
mv -i -- "$dir" "$parent/$new_base"
} || ret=$?
done
exit "$ret"' sh {} +
请注意,上面我添加了一个-i
至少mv
给用户一个选项来避免数据丢失,例如两个文件最终具有相同的新名称时。更好的方法是使用 zsh,zmv
它可以提前检查所有内容并具有试运行模式 ( -n
):
autoload -Uz zmv
zmv -n '(**/)(*)(#q/)' '$1$(ccase -t Kebab -- $2)'
默认情况下它还会忽略隐藏文件。它默认进行深度优先遍历。相当于.(#q/)
-type d
在这种情况下, 的退出状态ccase
将被忽略。当名称未更改时,不会尝试重命名。
您可以替换(*)
为(^*-*)
以避免处理名称中已包含连字符的文件。
您也可以在 zsh 中转换为 Kebab 大小写:
zmv -n '(**/)(*)(#q/)' \
'$1${${${2//[_[:space:]]##/-}//(#b)([^[:upper:].-])([[:upper:]])/$match[1]-$match[2]}:l}'
我们将所有空格或下划线序列转换为单个,在基本名称中的非大写字符(除了、 和)和大写字符之间-
插入s,并将整个结果转换为小写。如果您想重命名为而不是,则需要进行调整。-
.
-
l
AFileInTheUK.txt
a-file-in-the-u-k.txt
afile-in-the-uk.txt
答案2
逐个重命名目录并从最深的第一个开始。例如
#!/bin/ksh93
find . -type d -depth -print | while read DIR ; do
PREFIX=${DIR%/*}
SUFFIX=${ ccase -t Kebab "${.sh.match:1}"; }
print mv "${DIR}" "${PREFIX}/${SUFFIX}"
done
删除这个词打印在检查完脚本是否符合您的要求后,武装脚本;-)。
答案3
在这里。我正在寻找的命令是:
find . -depth -type d -execdir bash -c 'mv {} $(ccase -t Kebab {})' \;