Змусити sed запитувати підтвердження перед кожною заміною?


37

Чи є спосіб змусити sed запитати мене про підтвердження перед кожною заміною? Щось схоже на "c" при використанні заміни vim.

Сед робить це взагалі?


3
Це було б технічно можливо, але більше інтелектуальною вправою, ніж корисним. Див. Як зробити заміну тексту у великій ієрархії папок? який має рішення Vim та Perl.
Жил 'ТАК - перестань бути злим'

Я маю в використанні ВІМ (арг і argdo) всякий раз , коли мені потрібно «підтвердження», але було цікаво , якщо там був «простий» спосіб
Yuvi

1
Це суперечить основній меті sed - автоматизувати редагування в потоці.
теппік

@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або щось подібне.
користувач1717828

10
for i in $(grep -rl "old"); do vim -c "%s/old/new/gc" -c "wq" "$i"; done
tcpaiva

@tecepe, перетворіть його у відповідь. Дякую.
Нішант

1
@Nishant Це була б крихка відповідь, оскільки вона дискваліфікує будь-який файл, що містить пробіл у своєму імені чи шляху.
Кусалаланда

7

Ви можете отримати це, зробивши таке:

:%s/OLD_TEXT/NEW_TEXT/gc

Зокрема, додавання cпісля третього роздільника.

Зауважте, що опція 'c' працює лише у Vim; ви не зможете ним користуватися sedв командному рядку.


4
Для уточнення: це працює лише у Vim, а не у звичайному командному рядку sed.
Брендан

ОП позначено тегом 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

-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- то, крім вищезазначеного, він також просить вас замінити перший на другий рядок на рядок "для кожного рядка.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.