使用外部字符串操作工具重命名多个文件夹

使用外部字符串操作工具重命名多个文件夹

我在用案例

以下命令有效。

$ 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,并将整个结果转换为小写。如果您想重命名为而不是,则需要进行调整。-.-lAFileInTheUK.txta-file-in-the-u-k.txtafile-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 {})' \;

相关内容