将目录从 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
但是这个命令有多个问题。
- 它对文件和目录都起作用。我只想重命名目录
- 不递归循环
- 如果 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' _ {} +
如果能直接使用重命名命令就更好了。但我认为我们必须使用两个正则表达式。
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
但是这个命令有多个问题。
- 它对文件和目录都起作用。我只想重命名目录
- 不递归循环
- 如果 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' _ {} +