外部文字列操作ツールを使用して複数のフォルダの名前を変更する

外部文字列操作ツールを使用して複数のフォルダの名前を変更する

使っていますケース

次のコマンドが有効です。

$ 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十分です。
  • bashと同様に、shでもパラメータ拡張とコマンド置換は少なくともリストコンテキストで引用されなければなりません。それ以外の場合は分割+globを経ます!たとえば、参照してください。bash / POSIXシェルで変数を引用することを忘れてしまうセキュリティリスク
  • 特に、呼び出しなどの潜在的に破壊的なタスクを実行する場合は、mv実行するすべてのコマンドが成功したことを確認することをお勧めします。ここでは、失敗したディレクトリの|| continueスキップを使用します。ccase
  • +可能であれば、;複数のディレクトリを渡さないでください。shこれにより、ファイルごとのシェルが呼び出されるのを防ぎます。ただし、find非標準述語をサポートするすべての実装がそう-execdirするわけではありません。一部のBSDまたは以前のバージョンのfindGNUでは、+同じことをした;後にループすることが重複しています(ただし無害ですが)。
  • はすでに存在するmv -- SomeDir some_dirものと同じです。some_dirディレクトリまたはディレクトリへのシンボリックリンクですmv -- SomeDir some_dir/SomeDir。 GNU実装を使用すると、オプションを使用してこれを回避mvできます(または以下の方法を使用して競合を検出できます)。-Tzmv

これを回避し-execdir 、できるだけ少ない数のシェルを実行するには使用できますが、-execデフォルト名と親ディレクトリを区別する必要があります。改善として失敗を記録して終了ステータスccasemv反映することもできます。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 {} +

上記では、たとえば、2つのファイルが同じ新しい名前で終わる場合など、データの損失を防ぐためのオプションをユーザーに提供するオプションを追加し-iました。より良いアプローチは、すべてを事前に確認し、テスト実行モード()を提供するmvzshを使用することです。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

ディレクトリの名前を1つずつ変更し、最も深いディレクトリから始めます。例えば

#!/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 {})' \;

関連情報