Jak wykonać podstawienie w miejscu sed, które tworzy tylko kopie zapasowe zmienionych plików?

13

Uruchomiłem następujące polecenie, aby zastąpić termin używany we wszystkich plikach w bieżącym katalogu roboczym:

$ find . -type f -print0 | xargs -0 sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g'

Wykonało to zamianę słów, ale utworzyło także .buppliki, które nigdy nie miały Ms. Johnsonłańcucha.

Jak wykonać podstawienie bez tworzenia wszystkich tych niepotrzebnych kopii zapasowych?

Belmin Fernandez
źródło
wydaje mi się błędem sed see
Hotschke
Prawdopodobnie możliwe jest lepsze podejście przy użyciu exi warunkowo (tylko w przypadku zmiany pliku) uruchomione :!cp '%' '%.bup'przed zapisaniem i wyjściem. Może warto się przyjrzeć.
Wildcard,
Napisałem pytanie o mój pomysł powyżej na viwymianie stosu.
Wildcard,

Odpowiedzi:

6

Możesz sprawdzić zawartość plików, aby upewnić się, że nastąpi zamiana, gdy sedna nich zadziała :

find . \
    -type f \
    -exec grep -q 'Ms. Johnson' {} \; \
    -print0 |
xargs -0 sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g'

Jeśli chcesz być naprawdę mądry, możesz w ogóle zrezygnować z find:

grep -Z -l -r 'Ms. Johnson' |
    xargs -0 sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g'
amfetamachina
źródło
1
Myślę, że potrzebujemy '{}'tylko przed \;w tym -exec.
Jander
8

Znajdź ma -execoperatora, który wykonuje dowolne polecenie. Jeszcze lepiej -execjest testem , więc możesz -execpołączyć kilka s razem, a jeśli wcześniejsze polecenia zawiodą, późniejsze nie zostaną wykonane. Ciąg {}zostaje zastąpiony bieżącą nazwą pliku i ;oznacza koniec polecenia. Powinieneś podać oba, aby uniknąć zakłóceń powłoki.

Więc:

find . -type f \
    -exec grep -q 'Ms. Johnson' '{}' \; \
    -exec sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g' '{}' \;
Jander
źródło
Nigdy nie miałem problemu z używaniem nagich {}.
amfetamachina
1
@amphetamachine: Ja też nie, ale strona podręcznika to sugeruje. Może to być csh, lub mogą istnieć powłoki (nie Bash i nie POSIX), które rozwijają się {}do łańcucha zerowego podczas rozwijania nawiasów klamrowych.
Jander
4

Spojrzałem na stronę podręcznika i nie widziałem żadnego sposobu, aby to zrobić bezpośrednio sed, jak jestem pewien, że zrobiłeś to przed pytaniem. Widzę kilka sposobów obejścia tego za pomocą grep, ale myślę, że najłatwiej jest to:

grep -rlZ "Ms. Johnson" . | xargs -0 sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g'

-rrecurse
-ldrukuj nazwę pliku
-Zkończą tylko nazwy z null

Kevin
źródło
-1

Wygląda na to, że musisz najpierw utworzyć listę plików, które pasują do wyszukiwanych haseł.

search="test"
for match in $(find -type f -exec grep -l $search "{}" \;)
do sed -i'.bup' -e "/$search/ s/$search/Mrs. Melbin/g" "$match"
done
laebshade
źródło
Zapomniałeś .jako pierwszego argumentu find. Ponadto, jak poinformowano mnie nie tak dawno temu, tak naprawdę nie musisz cytować ani uciekać{}
Kevin,
Nie generuj listy plików i nie wykonuj na niej poleceń. Najpierw findgeneruje listę plików oddzieloną znakiem nowej linii, następnie powłoka dzieli tę listę w dowolnym miejscu spacji (nie tylko nowej linii), a następnie powłoka traktuje każde słowo jako wzorzec globalny. Działa to tylko wtedy, gdy nazwy plików nie zawierają białych znaków lub \[?*. Inne odpowiedzi na tej stronie pokazują, jak to zrobić poprawnie.
Gilles „SO- przestań być zły”
@Kevin .Jest niepotrzebny z find (przynajmniej find znaleziony na Linuksie), ponieważ zakłada się, że nie podano argumentu ścieżce.
laebshade