【问题标题】:Rename directories recursively from pascal or camel case to kebab case将目录从pascal或camel case递归重命名为kebab case
【发布时间】:2020-07-11 05:01:43
【问题描述】:

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

sed command that is working

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

figured out我们可以使用

for file in ./* ; do mv "$file" "$(echo $file|sed -e 's/\([A-Z]\)/-\L\1/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

【问题讨论】:

  • 请在您的问题中添加示例文件名(无描述、无图像、无链接)和该示例输入所需的文件名(无评论)。
  • 这可能对 GNU sed 有所帮助:sed 's/[A-Z]/-\L&/g; s/^-//'

标签: bash zsh


【解决方案1】:

zsh:

autoload -Uz zmv

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

如果输出正确,删除-n

【讨论】:

  • 调整全局限定符:(.od) 仅适用于普通文件; (od) 适用于任何类型。检查man zshexpn Glob Qualifiers 部分以了解许多其他可能性。需要od 排序,以便首先处理大多数嵌套(例如-depthfind)。
  • @blueray $f 是与zmv 匹配的文件名。可以应用一些修改(因为 zsh 可以嵌套参数扩展)。从最里面的##*/ 开始删除父路径(如果有)。然后进行全局替换(通过(#b) 启用反向引用)在每个大写字母前添加一个连字符。最外面的扩展将结果小写并删除单个前导连字符。一个改进是:${(L)${f##*/}//(#b)(?)([[:upper:]])/$match[1]-$match[2]}。添加带有M 标志的单独##*/ 以匹配任何现有父路径。
  • (?)([[:upper:]]) 不会处理重复的大写字母。例如。 IsIBad --> is-ibad
  • @blueray **/|* 是个坏主意。使用(**/)(*[[:upper:]]*)(/),并将第二个参数更改为${1}${(L)${2//(#b)([[:upper:]])/-$match[1]}#-}**/ 用于递归。将 dirname 和 basename 捕获为 $1$2(/) 是唯一需要的限定符,因为 od 已经在 zmv script 中。很抱歉发布错误的答案。
【解决方案2】:

find + 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:]]\)/\1-\2/g'\'' <<X | tr "[:upper:]" "[:lower:]"
${i##*/}
X
)
  echo mv -- "$i" "$parent$newname"
done' _ {} +

【讨论】:

  • s/\(.\)\([[:upper:]]\)/\1-\2/g 无法正确处理重复的大写字母。
【解决方案3】:

使用 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])/\1-\2/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 $1 ]] && { echo "Missing argument"  >&2; exit 1; }
cd "$1" && rendirrec

运行身份
$ ./rendirs top_of_the_directory_tree_to_be_renamed

【讨论】:

  • @blueray 现在似乎已更正。已添加bash 标签。
  • 是的,我回滚了。
  • @blueray bash标签又被删除了!
  • @blueray 代码中存在逻辑错误。现已更正
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-29
  • 1970-01-01
  • 1970-01-01
  • 2016-07-29
  • 2019-07-06
  • 2022-07-08
  • 2019-12-24
相关资源
最近更新 更多