Поиск и замена с терминала, но с подтверждением

Во многих текстовых редакторах вы можете выполнять поиск и замену, и вам предоставляется возможность проверить каждый найденный случай. Я хотел бы иметь возможность сделать что-то подобное в командной строке в Ubuntu. я знаю sed предлагает возможность находить и заменять строки в нескольких файлах, но есть ли способ подтвердить каждую замену пользователем?

В идеале я хотел бы получить решение, которое позволило бы мне выполнять поиск и замену по запросу для всех файлов в каталоге.

4 ответа

Решение

Как насчет Вима?

vim '+%s/set/bar/gc' some_file
  • + используется для запуска команды после загрузки файла.
  • % запускает команду по всему буферу (файлу).
  • gc флаги для :substitute, g для действия над всеми выражениями в строке и c для подтверждения каждой замены.

На самом деле вы не можете запретить Vim открывать файл, но вы можете автоматизировать сохранение и выход:

vim '+bufdo %s/set/bar/gc | up' '+q' some_file another_file
  • bufdo запускает команду над каждым буфером Это включает в себя часть после |, поэтому изменения в каждом буфере сохраняются (up).
  • q выходит, который выходит из Vim, так как мы находимся в последнем буфере.

Вы можете использовать комбинацию поиска и его -ok команда. Эта команда такая же, как -exec команда, но сначала спросите пользователя перед выполнением каждой указанной команды. Если пользователь согласен, запустите команду. В противном случае просто верните false.

от 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 > ?

это подскажет пользователю, как показано во второй строке выше.

Если вы введете y тогда будет выполнена команда замены sed, и будет произведена ваша замена. Если вы введете n тогда -ok команда игнорирует команду sed.


Если вы хотите сделать это "предложено" find а также replace для всех файлов в каталоге используйте команду следующим образом:

$ find /path/to/directory -type f -ok sed 's/foo/bar/' {} \;

Я бы просто написал небольшой скрипт на 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 и запустите его с шаблоном для совпадения в качестве первого аргумента, заменой в качестве второго и именем файла в качестве третьего:

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

Вы можете объединить предложение Муру использовать Vim с предложением AFSHIN использоватьfind (который имеет преимущество повторения в подкаталогах), например:

find ./ -type f -exec vim -c '%s/originalstring/replacementstring/gc' -c 'wq' {} \;
Другие вопросы по тегам