Grep untuk menemukan baris yang benar, sed untuk mengubah konten, lalu memasukkannya kembali ke file asli?

9

Saya mencoba mengubah satu kata pada baris tertentu dalam sebuah file, tetapi saya mengalami masalah saat menghubungkan semuanya.

Pada dasarnya, pada satu baris di file saya ada kata kunci 'firmware_revision', dan pada baris ini (dan hanya baris ini) saya ingin mengganti kata 'test' dengan kata 'production'.

Jadi saya bisa melakukan ini:

grep 'firmware_revision' myfile.py | sed 's/test/production'

Ini akan memilih baris yang saya inginkan dan melakukan substitusi, tetapi saya tidak tahu cara memasukkan baris baru ini ke file asli untuk mengganti baris yang lama. Saya jelas tidak bisa hanya mengarahkannya kembali ke file, jadi apa yang harus saya lakukan?

Bahkan jika saya menggunakan temporaries, dengan menggunakan grephanya untuk mendapatkan baris saya perlu saya kehilangan semua data lain dalam file, jadi saya tidak bisa lagi mengarahkan semua itu ke file temp kemudian ganti yang asli dengan temp.

Sunting - Seseorang meminta informasi lebih lanjut

Katakanlah saya punya file yang penuh dengan baris seperti ini

[
  ('key_name1', str, 'value1', 'Description'),
  ('key_name2', str, 'value2', 'Description'),
  ('key_name3', str, 'value3', 'Description'),
  ('firmware_revision', str, 'my-firmware-name-test', 'Firmware revision name')
]

sekarang saya ingin menulis skrip (idealnya satu baris) yang akan menemukan baris yang berisi 'firmware_revision', dan mengubah semua contoh kata 'tes' pada baris itu menjadi 'produksi'. Kata 'test' mungkin ada di tempat lain di file itu dan saya tidak ingin itu diubah. Jadi untuk menjadi jelas, saya ingin mengubah baris di atas menjadi

('firmware_revision', str, 'my-firmware-name-production', 'Firmware revision name')

Bagaimana saya melakukan ini?

John Allard
sumber
5
sedsangat kuat, ia dapat melakukan kedua fungsi ( grepdan substitusi) tetapi kami akan memerlukan lebih banyak info tentang bagaimana garis terlihat untuk membantu Anda.
grochmal
Saya akan menambahkan lebih banyak informasi ke posting asli
John Allard
7
Coba:sed -i.bak '/firmware_revision/ s/test/production/' myfile.py
John1024
1
Ah itu bekerja dengan sempurna! Bisakah Anda menjelaskan sintaksisnya?
John Allard
@ JohnAllard Sangat bagus. Saya menambahkan jawaban dengan penjelasan.
John1024

Jawaban:

22

Mencoba:

sed -i.bak '/firmware_revision/ s/test/production/' myfile.py

Di sini, /firmware_revision/bertindak sebagai syarat. Memang benar untuk baris yang cocok dengan regex firmware_revisiondan false untuk baris lainnya. Jika kondisinya benar, maka perintah berikut dijalankan. Dalam hal ini, perintah itu adalah perintah pengganti yang menggantikan kejadian pertama testdengan production.

Dengan kata lain, perintah s/test/production/dieksekusi hanya pada baris yang cocok dengan regex firmware_revision. Semua garis lainnya melewati tidak berubah.

Secara default, sed mengirimkan outputnya ke standard out. Namun, Anda ingin mengubah file di tempatnya. Jadi, kami menambahkan -iopsi. Secara khusus, -i.bakmenyebabkan file diubah di tempat dengan salinan cadangan disimpan dengan .bakekstensi.

Jika Anda telah memutuskan bahwa perintah itu berfungsi untuk Anda dan Anda ingin hidup dalam bahaya dan tidak membuat cadangan, maka, dengan GNU sed (Linux), gunakan:

sed -i '/firmware_revision/ s/test/production/' myfile.py

Sebaliknya, pada BSD (OSX), -iopsi harus memiliki argumen. Jika Anda tidak ingin menyimpan cadangan, berikan argumen kosong. Jadi, gunakan:

sed -i '' '/firmware_revision/ s/test/production/' myfile.py

Edit

Dalam pengeditan pertanyaan, OP meminta setiap kemunculan testdi telepon untuk diganti production. Dalam hal itu, kami menambahkan gopsi ke perintah pengganti untuk pengganti global (untuk jalur itu):

sed -i.bak '/firmware_revision/ s/test/production/g' myfile.py
John1024
sumber
5
Untuk kelengkapan, Anda mungkin ingin menjelaskan -i.bakbagian itu juga.
Stephen Harris
5
@StephenHarris Poin bagus. Penjelasan -iditambahkan.
John1024
Saya merasa Anda mungkin dapat mencapai bintang-bintang, hanya berdiri di atas buku yang menjelaskan semua hal yang seddapat dilakukan ...
KlaymenDK
@ John1024 Untuk yang bukan BSD, dapatkah Anda menambahkan alasan ''setelah yang pertama -i?
Hastur
3
OP ingin mengubah semua contoh kata 'tes' pada baris itu menjadi 'produksi' . Sangat penting untuk menambahkan / g ke perintah sed dalam kasus ini:, sed -i '/firmware_revision/ s/test/production/g' myfile.pyjika tidak, hanya instance pertama yang diubah.
Knub
4

Pada komputer lama dengan old-school sedyang tidak mendukung opsi -i:

TF=$( mktemp -t "${0##*/}"_$$_XXXXXXXX ) && \
trap 'rm -f "$TF"' EXIT HUP INT QUIT TERM && \
sed '/firmware_revision/ s/test/production/' myfile.py >"$TF" && \
mv -f "$TF" myfile.py
Satō Katsura
sumber
1
Pada mesin-mesin yang lebih tua, Anda cukup menggunakan eddan melakukannya dalam satu baris:printf %s\\n g/firmware_revision/s/test/production/g w q | ed -s myfile.py
don_crissti
1
@don_crissti Sangat benar itu, tetapi ed(1)tidak menawarkan alasan untuk menunjukkan penggunaan mktemp(1).
Satō Katsura
Jika Anda menggunakan tempfile, Anda dapat menyimpan inode dan perms dari file asli, sama seperti edhalnya. Alih-alih mv -f "$TF" myfile.pl, gunakan cat "$TF" > myfile.pl && rm -f "$TF". BTW, itu praktik yang baik untuk menggunakan nama variabel huruf kecil ( $tfbukan $TF), mereka dijamin tidak akan bertentangan dengan bash built-in vars (mungkin shell bourne lainnya juga).
cas
@cas Re:: cat "$TF" > myfile.plcoba ini: touch a b; chmod 0444 a; cat b >a(BTW, sed -itidak akan berfungsi dalam kasus itu, baik). Lebih baik biarkan pengguna berurusan dengan itu. Re:: rm -f "$TF"tidak diperlukan, lihat trap ... EXIT. Re: $tfbukannya $TF: mungkin; ini masalah gaya.
Satō Katsura
Itu perilaku normal dan yang diharapkan dari izin file unix. Maksud saya adalah bahwa membuat file baru dan mv-ing itu di atas yang asli 1. akan menghasilkan inode baru untuk nama file itu (memutus semua tautan keras). 2. mungkin mengubah perms, jika saat ini umask berbeda dari perms asli. Adapun vars huruf besar, itu bukan hanya masalah gaya - banyak orang yang telah membuat kesalahan dengan menggunakan PATH huruf besar atau ACAK atau SHELL atau apa pun untuk variabel mereka sendiri telah menemukan cara yang sulit. (dan ya, saya sering menggunakan huruf besar sendiri. Saya tahu itu salah, saya mencoba untuk menghentikan kebiasaan itu).
cas