将目录从 pascal 或 camel 大小写递归重命名为 kebab 大小写

Rename directories recursively from pascal or camel case to kebab case

如果能直接使用重命名命令就更好了。但我认为我们必须使用两个正则表达式。

sed command that is working

% echo MyExPression | sed --expression 's/\([A-Z]\)/-\L/g' --expression 's/^-//'                         
my-ex-pression
% echo myExPression | sed --expression 's/\([A-Z]\)/-\L/g' --expression 's/^-//'
my-ex-pression

figured out我们可以用

for file in ./* ; do mv "$file" "$(echo $file|sed -e 's/\([A-Z]\)/-\L/g' -e 's/^.\/-//')" ; done

但是这个命令有多个问题。

  1. 它对文件和目录都起作用。我只想重命名目录
  2. 不递归循环
  3. 如果 kebab case 文件名已经存在,那么它说 mv: cannot move './first-folder-to-rename' to a subdirectory of itself, './first-folder-to-rename/first-folder-to-rename'

那么,这里的解决方案可能是什么?

更新 1

这是一个示例目录结构

% tree
.
├── EmptyFile.txt
├── FirstDirectoryName
│   ├── FourthDirectoryName
│   ├── secondDirectoryName
│   └── thirdDirectoryName
├── FourthDirectoryName
├── secondDirectoryName
└── thirdDirectoryName

预期输出

% tree
.
├── EmptyFile.txt
├── first-directory-name
│   ├── fourth-directory-name
│   ├── second-directory-name
│   └── third-directory-name
├── fourth-directory-name
├── second-directory-name
└── third-directory-name

zsh:

autoload -Uz zmv

zmv -n -Q '**/|*[[:upper:]]*(/od)' \
'${(M)f##*/}${(L)${${f##*/}//(#b)([[:upper:]])/-$match[1]}#-}'

如果输出正确,则删除 -n

使用 bash 和 GNU sed:

$ cat rendirs

#!/bin/bash

# function for renaming directories recursively
rendirrec () {
    local from to
    for from in *; do
        [[ -d $from && ! -h $from ]] || continue
        to=$(sed -E 's/([^A-Z])([A-Z])/-/g; s/.*/\L&/' <<< "$from")
        cd "$from" && { rendirrec; cd ..; } || exit
        [[ $to = "$from" ]] && continue
        if [[ -e $to ]]; then
            printf "'%s' already exists. Skipping..\n" "$to" >&2
            continue;
        fi
        mv "$from" "$to" || exit
    done
}

[[ -z  ]] && { echo "Missing argument"  >&2; exit 1; }
cd "" && rendirrec

运行 作为
$ ./rendirs top_of_the_directory_tree_to_be_renamed

查找+shell+sed+tr:

find . -depth -type d -name '*[[:upper:]]*' -exec sh -c '
for i do
  parent=${i%/*}
  [ "$parent" = "$i" ] && parent="" || parent="$parent/"
  newname=$(sed '\''s/\(.\)\([[:upper:]]\)/-/g'\'' <<X | tr "[:upper:]" "[:lower:]"
${i##*/}
X
)
  echo mv -- "$i" "$parent$newname"
done' _ {} +