web-dev-qa-db-ja.com

ターミナルからの検索と置換、ただし確認付き

多くのテキストエディターでは、検索と置換を行い、見つかった各ケースを検査するオプションが与えられます。 Ubuntuのコマンドラインでこれに似たことができるようになりたいです。 sedには複数のファイルで文字列を検索して置換する機能がありますが、ユーザーがそれぞれの置換を確認する方法はありますか?

理想的には、ディレクトリ内のすべてのファイルでこの「プロンプト」検索と置換を実行できるソリューションが欲しいです。

Vimはどうですか?

vim '+%s/set/bar/gc' some_file
  • +は、ファイルのロード後にコマンドを実行するために使用されます。
  • %は、バッファー(ファイル)全体に対してコマンドを実行します。
  • gcは、:substitutegのフラグであり、行内のすべての式に対して作用し、cは各置換を確認します。

実際にVimがファイルを開くのを防ぐことはできませんが、保存と終了を自動化できます。

vim '+bufdo %s/set/bar/gc | up' '+q' some_file another_file
  • bufdo は、各バッファーに対してコマンドを実行します。これには|の後の部分が含まれるため、各バッファーへの変更が保存されます( up )。
  • qは終了します。これで、最後のバッファーにいるためVimが終了します。
6
muru

find とその-okコマンドの組み合わせを使用できます。このコマンドは-execコマンドと同じですが、指定された各コマンドを実行する前にユーザーに最初に尋ねます。ユーザーが同意したら、コマンドを実行します。それ以外の場合は、単にfalseを返します。

from man find

-ok command ;
      Like  -exec  but ask the user first. If the user agrees, run the command. 
      Otherwise just return false. If the command is run, its standard input is 
      redirected from /dev/null.

したがって、次のようにコマンドを使用できます。

$ find ./ -name filename -ok sed 's/foo/bar/' {} \;
< sed ... filename > ?

これにより、上の2行目に示すようにユーザーにプロンプ​​トが表示されます。

yと入力すると、sed replacementコマンドが実行され、置換が行われます。 nと入力すると、-okコマンドはsedコマンドを無視します。


ディレクトリ内のすべてのファイルでこの「プロンプト」findおよびreplaceを実行するには、次のようにコマンドを使用します。

$ find /path/to/directory -type f -ok sed 's/foo/bar/' {} \;
4
αғsнιη

ちょっとしたPerlスクリプトを書くだけです。

#!/usr/bin/env Perl
use strict;
use warnings;

my $pat="$ARGV[0]";
my $replacement=$ARGV[1];
my $file="$ARGV[2]";

## Open the input file
open(my $fh, "$file");

## Read the file line by line
while (<$fh>) {
    ## If this line matches the pattern
    if (/$pat/) {
        ## Print the current line
        print STDERR "Line $. : $_";
        ## Prompt the user for an action
        print STDERR "Substitute $pat with $replacement [y,n]?\n";
        ## Read the user's answer
        my $response=<STDIN>;
        ## Remove trailing newline
        chomp($response);
        ## If the answer is y or Y, make the replacement.
        ## All other responses will be ignored. 
        if ($response eq 'Y' || $response eq 'y') {
            s/$pat/$replacement/g;
        }    
    }
    ## Print the current line. Note that this will 
    ## happen irrespective of whether a replacement occured.
    print;
}

ファイルを~/bin/replace.plとして保存し、chmod a+x ~/bin/replace.plで実行可能にし、最初の引数として一致するパターン、2番目として置換、3番目としてファイル名で実行します。

replace.pl foo bar file > newfile

すべての*.txtファイルなど、複数のファイルで実行するには、bashループでラップします。

for file in *txt; do 
    replace.pl foo bar "$file" > "$file".new
done

そして、「インプレース」でファイルを編集するには:

for file in *txt; do 
    tmp=$(mktemp)
    replace.pl foo bar "$file" > "$tmp" && mv "$tmp" "$file"
done
2
terdon

vimを使用するmuruの提案AFSHINのfindを使用する提案 (サブディレクトリに再帰するという利点があります)を組み合わせることができます。例:

find ./ -type f -exec vim -c '%s/originalstring/replacementstring/gc' -c 'wq' {} \;
0
Anurag Bihani