Cara melaporkan perubahan "sed" di tempat

23

Saat menggunakan seduntuk mengganti string di tempat, apakah ada cara untuk membuatnya melaporkan perubahan yang dilakukannya (tanpa bergantung pada file lama dan baru yang berbeda)?

Misalnya, bagaimana saya bisa mengubah baris perintah

find . -type f | xargs sed -i 's/abc/def/g'

jadi saya bisa melihat perubahan yang dilakukan dengan cepat?

ricab
sumber

Jawaban:

15

Anda bisa menggunakan sed's wbendera dengan baik /dev/stderr, /dev/tty, /dev/fd/2jika didukung pada sistem Anda. Misalnya dengan input fileseperti:

foo first
second: missing
third: foo
none here

berlari

sed -i '/foo/{
s//bar/g
w /dev/stdout
}' file

output:

bar first
third: bar

meskipun filekonten diubah menjadi:

bar first
second: missing
third: bar
none here

Jadi dalam kasus Anda, jalankan:

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
s//bar/g
w /dev/fd/2
}' {} \;

akan mengedit file di tempat dan keluaran:

./file1:
barang bar
lebih banyak bar

./file2:

./file3:
bar dulu
ketiga: bar

Anda juga dapat mencetak sesuatu seperti original line >>> modified linemisalnya:

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
h
s//bar/g
H
x
s/\n/ >>> /
w /dev/fd/2
x
}' {} \;

edit file di tempat dan keluaran:

./file1:
hal-hal foo >>> hal-hal bar
lebih banyak foo >>> lebih banyak bar

./file2:

./file3:
foo dulu >>> bilah dulu
ketiga: foo >>> ketiga: bar
don_crissti
sumber
16

Anda bisa melakukannya dalam dua lintasan menggunakan paksi rintisan pada lintasan pertama dengan:

find . -type f | xargs sed --quiet 's/abc/def/gp'

di mana --quietmembuat sed tidak menunjukkan setiap baris dan pakhiran hanya menunjukkan garis di mana substitusi telah cocok.

Ini memiliki batasan bahwa sed tidak akan menunjukkan file mana yang sedang diubah yang tentu saja dapat diperbaiki dengan beberapa kompleksitas tambahan.

msw
sumber
Itu tidak cukup melakukan apa yang saya inginkan karena Anda perlu dua melewati, tapi saya memilih karena saya baru belajar hal ini dan itu sangat berguna. Terutama jika, seperti yang Anda katakan, file-file itu juga dicetak. Sesuatu sepertifor x in `find . -type f`; do echo ///File $x: ; sed --quiet 's/abc/def/gp' $x; done
ricab
@ msw Saya punya banyak sedekspresi, saya tidak ingin menulis /gpuntuk masing-masing. Bagaimana cara mengaturnya secara global?
alhelal
8

Saya tidak berpikir itu mungkin, tetapi solusi mungkin menggunakan perl sebagai gantinya:

find . -type f | xargs perl -i -ne 's/abc/def/ && print STDERR' 

Ini akan mencetak garis yang diubah ke kesalahan standar. Sebagai contoh:

$ cat foo
fooabcbar
$ find . -type f | xargs perl -i -ne 's/abc/def/ && print STDERR' 
foodefbar

Anda juga dapat membuat ini sedikit lebih rumit, mencetak nomor baris, nama file, baris asli dan baris yang diubah:

$ find . -type f | 
   xargs perl -i -ne '$was=$_; chomp($was);
                      s/abc/def/ && print STDERR "$ARGV($.): $was : $_"' 
./foo(1): fooabcbar : foodefbar
terdon
sumber
+1 Anda juga dapat menggunakan $ARGVnama file yang sedang dioperasikan.
Joseph R.
@ JosephephR. Poin bagus, ditambahkan.
terdon
Terima kasih, itu mungkin membantu (tidak menguji sendiri). Saya tidak memilih karena tidak menggunakan sed, jadi itu tidak menjawab pertanyaan.
ricab
@ricab tidak apa-apa, Anda seharusnya tidak menerima jawaban kecuali itu benar-benar menjawab pertanyaan Anda. Ingatlah bahwa untuk hal-hal sederhana seperti ini, perlsintaksinya sangat mirip dengan seddan saya tidak berpikir apa yang Anda minta sebenarnya mungkin terjadi sed.
terdon
3

Dimungkinkan menggunakan flag w yang menulis pola saat ini ke file. Jadi dengan menambahkannya ke perintah pengganti kita dapat melaporkan penggantian berturut-turut ke file dan mencetaknya setelah pekerjaan selesai. Saya juga suka mewarnai string yang diganti dengan grep.

sed -i -e "s/From/To/gw /tmp/sed.done" file_name
grep --color -e "To" /tmp/sed.done

Perhatikan, bahwa harus ada hanya satu ruang antara w dan nama file-nya.

Ini bahkan lebih baik daripada diff, karena diffing juga dapat menunjukkan perubahan meskipun mereka tidak dibuat oleh sed.

Mendongkrak
sumber
Hanya mengujinya dan hanya menulis baris yang disubstitusikan sed.done. Garis dengan "Untuk" di file asli karena itu tidak dicetak untuk sed.done, sehingga ketika Anda grep "To" sed.doneAnda hanya akan melihat garis-garis yang sedang diubah oleh sed. Anda tidak akan melihat baris asli dalam file sebelum diganti, jika itu yang Anda tuju ...
aairey
1

Saya suka solusi @terdon - perl bagus untuk ini.

Inilah versi tweak saya yang:

  • tidak akan mencoba mengubah file yang tidak memiliki string yang cocok
  • akan mencadangkan versi sebelum file yang berubah (buat versi .bak)
  • akan mencantumkan setiap file / lineno yang diubah, dan menampilkan versi LAMA dan BARU dari baris yang diindentasi dan di bawahnya untuk dibaca dengan mudah

kode

find /tmp/test -type f ! -name "*.bak" -exec grep -l '/opt/gridmon' {} \; | xargs -L1 perl -ni'.bak' -e'$old=$_; s/\/opt\/gridmon/~/g && print STDERR "$ARGV($.):\n\tOLD:$old\tNEW:$_"'

contoh output

/tmp/test/test4.cfg(13):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
/tmp/test/test4.cfg(24):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
andy987
sumber