ファイル名とディレクトリ名の文字列を再帰的に置き換える方法は?

ファイル名とディレクトリ名の文字列を再帰的に置き換える方法は?

私はetckeeperのgitレプリカを持っており、etckeeper名前はusrkeeper./foo-etckeeper-bar./foo-usrkeeper-bar

関連ファイルを見つけるのは簡単です。

% find . -path '*etckeeper*' -print

しかし、実際に名前を変更する方法がわかりません。私は次のものxargsと組み合わせようとしましたmv

% find . -path '*etckeeper*' -print0 | xargs -0 -n 1 -J % bash -c mv % '$(echo' % \| sed \"s/etckeeper/usrkeeper/\" \)

読みやすくするためにエスケープされていない後半は、次のように表示されますxargs -0 -n 1 -J % bash -c mv % $(echo % | sed "s/etckeeper/usrkeeper/" )。これに関するアイデアは、置き換えのために$()ファイル名をパイプすることですsed

ここでの問題は、bash -c実行に必要なコマンドが単一の文字列であることです。その後、パラメータを位置パラメータとして解釈し始めます。私はすべての内容を引用することができます:

% find . -path '*etckeeper*' -print0 | xargs -0 -n 1 -J % bash -c 'mv % $(echo % | sed "s/etckeeper/usrkeeper/g" )'

しかし、今はxargs交換されません%。この問題をどのように解決できますか? (また、参照として、etckeeper名前を含むファイルが name を含むディレクトリに存在する場合、ディレクトリがファイルの前に移動されるetckeeperため、上記の操作は失敗します。)

ベストアンサー1

名前変更コマンドを使用できます(参照:編集1)。

ソリューション1

合理的な数のファイル/ディレクトリの場合は、bash 4オプションを設定してグローバルスター(再帰名には適用されません。編集3):

shopt -s globstar
rename -n 's/etckeeper/userkeeper/g' **

ソリューション2

ファイル/ディレクトリの数が多い場合は、名前変更と検索を2つの手順で使用して、名前を変更したばかりのディレクトリでファイルの名前変更が失敗するのを防ぎます(参照)。編集2):

find . -type f -exec rename 's/etckeeper/userkeeper/g' {} \;
find . -type d -exec rename 's/etckeeper/userkeeper/g' {} \;

編集1:

2つの名前変更コマンドがあります。。この答えは、Debianベースのディストリビューションで利用可能なPerlベースの名前変更コマンドを使用します。 Debian ベースではなくディストリビューションにインストールするには、次のものが必要です。cpanからインストール可能またはキャッチ。

編集2:

-depthJeff Schallerがコメントでfindオプションを指摘したように、オプションProcess each directory's contents before the directory itselfごとに「ソートされた」ルックアップで-depth十分です。

find . -depth -exec rename 's/etckeeper/userkeeper/g' {} \;

編集3

etckeeper/etckeeper/etckeeper解決策1は、外部レベルが内部レベルよりも前に処理され、内部レベルへのポインタが役に立たなくなるため、再帰的な名前変更ターゲット(たとえば)には機能しません。 (最初の名前の後にetckeeper/etckeeper/etckeeper名前が付けられるため、usrkeeper/etckeeper/etckeeper名前をetckeeper/etckeeper/およびに置き換えるとetckeeper/etckeeper/etckeeper失敗します)。オプションはfind同じ問題を解決します-depth

編集4

Casがコメントで指摘したように、{}\;代わりに {}+ を使用します。 - Perlスクリプトをフォークすると(たとえば、ファイル/ディレクトリごとに1回)、費用がかかります。

おすすめ記事