大規模なフォルダ階層でテキストを置き換える方法は?

大規模なフォルダ階層でテキストを置き換える方法は?

複数のファイルから特定のテキスト(特定のインスタンスを除く)を検索して置き換えたいです。各行に対して、その行を置き換える必要があるかどうかを尋ねるメッセージが表示されます。 vimに似ていますが、:%s/from/to/gcc確認メッセージを含む)フォルダセットにまたがっています。使用できる良いコマンドラインツールやスクリプトはありますか?

答え1

なぜvimを使用しないのですか?

vimはすべてのファイルを開きます

vim $(find . -type f)

または、関連ファイルを開きます(Calebが提案したように)。

vim $(grep 'from' . -Rl)

その後、すべてのバッファで置換を実行します。

:bufdo %s/from/to/gc | update

sedを使用してこれを行うこともできますが、sed私のsedの知識は限られています。

答え2

-l -pe引数として渡されたファイル()を1行ずつ()置き換えるように指示する小さなPerlスクリプトを使用して、簡単な操作を実行できます-i

perl -i -l -pe '
    if (/from/) {                            # is the source text present on this line?
        printf STDERR ("%s: %s [y/N]? ", $ARGV, $_);  # display a prompt
        $r=<STDIN>;                                   # read user response
        if ($r =~ /^[Yy]/) {                          # if user entered Y:
            s/from/to/g;                              # replace all occurences on this line
    }' /path/to/files

可能な改善方法は、プロンプトセクションに色を付け、「現在のファイルのすべてのイベントを置き換える」などの機能をサポートすることです。各イベントを連続して個別に表示することはより困難な場合があります。

2番目の部分はファイルマッチングです。関連ファイルが多すぎず、zshを実行している場合は、現在のディレクトリとサブディレクトリのすべてのファイルを再帰的に一致させることができます。

perl -i -l -pe '…' **/*(.)

シェルが bash ≥4 の場合は実行できますが、perl … **/*sed がディレクトリで実行しようとして失敗するため、偽のエラーメッセージが生成されます。ファイルセット(Cファイルなど)内でのみ置換を実行したい場合は、一致を制限できます(bash ≥4またはzshで動作します)。

perl -i -l -pe '…' **/*.[hc]

置き換えられるファイルに対してさらに制御が必要な場合、シェルに再帰的なディレクトリ一致設定がない場合は、ファイルが多すぎて「コマンドラインが長すぎます」エラーが発生した場合に**使用してくださいfind。たとえば、名前の付いたすべてのファイル、または現在のディレクトリと*.hそのサブディレクトリで置き換えを実行する場合(以前のシステムでは行末で代わりに*.c使用する必要があります(この形式は高速ですが、どこでも利用できるわけではありません)。\;++

find . -type f -name '*.[hc]' -exec perl -i -l -pe '…' {} +

しかし、対話が必要な場合は、対話型エディタを使用します。GertがVimでこれを行う方法を示します。、検索したいすべてのファイルを開く必要がありますが、多くのファイルが問題になる可能性があります。

Emacsでは、次のことができます。

  1. M-x find-name-dired(最上位ディレクトリの指定)またはM-x find-dired(コマンドラインの指定)を使ってファイル名を収集しますfind
  2. その結果疲れるバッファリングして[tすべてのファイルを表示]をクリックし、Q( dired-do-query-replace-regexp)表示されたファイルをすばやく交換します。

答え3

sdiff(望むよりhttp://www.gnu.org/software/diffutils/manual/diffutils.html#Invoking-sdiff)ここで役に立ちます。これによりインタラクティブに作業できます。したがって、これを行うために交換操作を実行することによって生成された一時ファイルを使用することがsed可能な解決策かもしれません。

# use file descriptor 3 to still allow use of stdin
while IFS= read -r -d '' file <&3; do

  # write the result of the replacement into a temporary file
  sed -r 's/something/something_else/g' -- "$file" > replacer_tmp

  if cmp -s -- "$file" replacer_tmp; then
    continue; # nothing was replaced
  fi

  echo "There is something to replace in '$file'! Starting interactive diff."
  echo

  sdiff -o "$file" -s -d -- "$file" replacer_tmp

  echo

done 3< <(find . -type f -print0)

(POSIX以外のプロセスで置き換えられ、サポートされているread -dファイルループを使用してくださいbash。)

関連情報