Czy sed pyta o potwierdzenie przed każdą wymianą?

37

Czy istnieje sposób, aby sed pytał mnie o potwierdzenie przed każdą wymianą? Coś podobnego do „c” przy użyciu polecenia replace inside vim.

Czy sed w ogóle to robi?

Yuvi
źródło
3
Byłoby to technicznie możliwe, ale bardziej intelektualne ćwiczenie niż pożyteczne przedsięwzięcie. Zobacz Jak zrobić zamianę tekstu w hierarchii dużych folderów? który ma rozwiązania Vima i Perla.
Gilles „SO- przestań być zły”
Ja jestem przy użyciu vim (args i argdo) ilekroć muszę „potwierdzenie”, ale zastanawiałem się, czy nie było „” prostszy sposób
YuVI
1
Jest to sprzeczne z podstawowym celem sed - zautomatyzowania edycji w strumieniu.
teppic
@teppic nie jest tak naprawdę, ponieważ nadal musisz znaleźć instancje w plikach tekstowych, które sed może dla Ciebie zrobić. Myślę, że pytanie ma sens, na wypadek, gdybyś dodał niewłaściwe pliki do listy i chciał zobaczyć, który plik edytujesz
Kolob Canyon

Odpowiedzi:

37

Wykonanie tego sedprawdopodobnie nie byłoby możliwe, ponieważ jest to nieinteraktywny edytor strumieni. Owijanie sedskryptu wymagałoby o wiele za dużo myślenia. Łatwiej jest to zrobić za pomocą vim:

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

Ponieważ zostało to wspomniane w komentarzach poniżej, oto jak zostanie to zastosowane w wielu plikach pasujących do określonego wzorca globowania nazw plików w bieżącym katalogu:

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

Lub, jeśli najpierw chcesz się upewnić, że plik naprawdę zawiera linię, która pasuje do podanego wzorca, przed wykonaniem podstawienia,

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

Dwie powyższe pętle powłoki zostały zmodyfikowane w findkomendy, które robią to samo, ale dla wszystkich plików o określonej nazwie gdzieś w lub w jakimś top-dirkatalogu,

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' {} \;

Lub przy użyciu oryginalnych pętli powłoki i findwprowadzeniu do nich ścieżek kanału:

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 {} +

Ty, w każdym razie nie chcą zrobić coś takiego for filename in $( grep -rl ... )od

  1. wymagałoby to grepzakończenia działania jeszcze przed rozpoczęciem pierwszej iteracji pętli, która jest nieelegancka , i
  2. zwrócone nazwy ścieżek grepzostaną podzielone na słowa na białych spacjach, a słowa te zostaną poddane globalizacji nazw plików (dyskwalifikuje ścieżki zawierające spacje i znaki specjalne).

Związane z:

Kusalananda
źródło
2
Zaletą sedjest to, że może działać na wielu plikach, np. sed -i 's/old/new/g' /path/to/*.txtLub coś podobnego.
user1717828
10
for i in $(grep -rl "old"); do vim -c "%s/old/new/gc" -c "wq" "$i"; done
tcpaiva
@tecepe, proszę przekonwertować na odpowiedź, dziękuję.
Nishant,
1
@Nishant Byłaby to delikatna odpowiedź, ponieważ zdyskwalifikowałaby każdy plik zawierający białe znaki w nazwie lub ścieżce.
Kusalananda
7

Możesz to zrobić, wykonując takie czynności:

:%s/OLD_TEXT/NEW_TEXT/gc

W szczególności dodanie ctrzeciego separatora po.

Zauważ, że opcja „c” działa tylko w Vimie; nie będziesz mógł używać go sedw wierszu poleceń.

Kevin M.
źródło
4
Aby wyjaśnić: działa to tylko w Vimie, a nie w zwykłym wierszu poleceń sed.
Brendan
OP jest oznaczony vim, więc ta odpowiedź ma zastosowanie; jednak wyraźnie vim i sed mają różne umiejętności
ILMostro_7
-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
}

Jeśli podajesz to z jednym argumentem - searchandreplace "AAA"- wyszukuje tylko ten ciąg w bieżącym katalogu, ale jeśli podajesz go z podwójnymi argumentami - searchandreplace AAA BBB- to oprócz powyższego prosi również o zastąpienie pierwszego z drugiego wiersza „ linia ”dla każdej linii.

LyXTeX
źródło