Заставить sed спрашивать подтверждение перед каждой заменой?

37

Есть ли способ заставить sed спрашивать у меня подтверждение перед каждой заменой? Нечто похожее на 'c' при использовании замены внутри vim.

Сед делает это вообще?

Yuvi
источник
3
Это было бы технически возможно, но скорее интеллектуальным упражнением, чем полезным делом. См. Как выполнить замену текста в большой иерархии папок? который имеет решения Vim и Perl.
Жиль "ТАК - перестань быть злым"
Я имею в использовании ВИМ (арг и argdo) всякий раз , когда мне нужно «подтверждение», но было интересно , если там был «простой» способ
Yuvi
1
Это идет вразрез с основной целью sed - автоматизировать редактирование над потоком.
teppic
@teppic не совсем, потому что вам все равно придется искать экземпляры в текстовых файлах, что может сделать для вас sed. Я думаю, что вопрос имеет смысл, на тот случай, если вы добавили неправильные файлы в список и хотели посмотреть, какой файл вы редактировали
Колоб Каньон,

Ответы:

37

Делать это с sed, вероятно, было бы невозможно, поскольку это неинтерактивный редактор потоков. Завершение sedв сценарии потребовало бы слишком много размышлений. Проще сделать это с помощью vim:

vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' file.in

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

for fname in file*.txt; do
    vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done

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

for fname in file*.txt; do
    grep -q 'PATTERN' "$fname" &&
    vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done

Вышеупомянутые два findцикла оболочки, модифицированные в команды, которые делают то же самое, но для всех файлов с определенным именем где-то в или в некотором top-dirкаталоге,

find top-dir -type f -name 'file*.txt' \
    -exec vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' {} \;
find top-dir -type f -name 'file*.txt' \
    -exec grep -q 'PATTERN' {} \; \
    -exec vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' {} \;

Или, используя оригинальные циклы оболочки и findдобавляя в них имена путей:

find top-dir -type f -name 'file*.txt' -exec sh -c '
    for pathname do
        vim -c "%s/PATTERN/REPLACEMENT/gc" -c "wq" "$pathname"
    done' sh {} +
find top-dir -type f -name 'file*.txt' -exec sh -c '
    for pathname do
        grep -q "PATTERN" "$pathname" &&
        vim -c "%s/PATTERN/REPLACEMENT/gc" -c "wq" "$pathname"
    done' sh {} +

Вы, в любом случае, не хотите сделать что - то вроде for filename in $( grep -rl ... )так

  1. для этого потребуется завершить grepработу еще до начала первой итерации цикла, которая не является элегантной , и
  2. возвращаемые путем пути grepбудут разбиты на слова в пробелах, и эти слова будут подвергнуты глобализации имени файла (это дисквалифицирует пути, содержащие пробелы и специальные символы).

Связанный:

Кусалананда
источник
2
Преимущество в sedтом, что он может работать с несколькими файлами, например, sed -i 's/old/new/g' /path/to/*.txtили с чем-то похожим.
user1717828
10
for i in $(grep -rl "old"); do vim -c "%s/old/new/gc" -c "wq" "$i"; done
tcpaiva
@ Tecepe, пожалуйста, преобразовать его в ответ, спасибо.
Nishant
1
@Nishant Это был бы хрупкий ответ, поскольку он дисквалифицировал бы любой файл, содержащий пробел в его имени или пути.
Кусалананда
7

Вы можете получить это, сделав так:

:%s/OLD_TEXT/NEW_TEXT/gc

В частности, добавление cпосле третьего разделителя.

Обратите внимание, что опция 'c' работает только в Vim; вы не сможете использовать его sedв командной строке.

Кевин М
источник
4
Чтобы уточнить: это работает только в Vim, а не в обычной командной строке sed.
Брендан
OP помечен vim, поэтому этот ответ применим; хотя явно у vim и sed разные способности
ILMostro_7
4

Вы можете разрешить sedделать свою работу с файлом и затем сохранить результат во временном файле, который затем можно интерактивно вставить в исходный файл, используя sdiff(см. Http://www.gnu.org/software/diffutils/manual/diffutils.html. # Invoking-sdiff ):

sed -r 's/something/something_else/g' my_file > tmp_file
sdiff -o my_file -s -d my_file tmp_file
PHK
источник
-1
searchandreplace () {
if [ -z $2 ] ; then
    realpath * | xargs grep -ins --directories=skip --color=always "$1" | nl -s "."
else
    realpath * | xargs grep -ins --directories=skip --color=always "$1" | nl -s "."
    exp=$(realpath * | xargs grep -ins --directories=skip --color=never "$1" | cut -d':' -f1,2 | awk -F':' '{print $2 $1}' | sed -E "s|^[0-9]+|sed -i \'&s/|; s|//|/"$1"/"$2"/g\' /|")
    IFS=$'\n'
    for d in $exp
        do
        read -p "Exec $(echo -e -n "\033[1;31m$d\033[0m")? " -e REP
            if [ "$REP" = y ]; then
            echo `bash -c $d`;
            fi
    done
fi
}

Если вы передаете это с одним аргументом - searchandreplace "AAA"он ищет только эту строку в текущем каталоге, но если вы передаете его с двойными аргументами searchandreplace AAA BBB- то в дополнение к вышесказанному он также просит заменить первую строку на " линия "для каждой строки.

LyXTeX
источник