sed: bagaimana cara mengganti baris jika ditemukan atau menambahkan ke akhir file jika tidak ditemukan?


34

Dengan file input tunggal yang hanya berisi komentar (dimulai dengan #) dan VARIABEL = baris nilai, apakah mungkin untuk mengganti nilai untuk variabel tunggal jika ditemukan dan, jika tidak, menambahkan pasangan ke akhir file jika tidak ditemukan?

Metode saya saat ini bekerja dengan menghapusnya di pass pertama, lalu menambahkannya ke akhir file di pass kedua, tetapi metode ini mengacaukan urutan baris (dan juga dua perintah berbeda):

sed -r "/^FOOBAR=.*$/d"      -i samefile &&
sed -r "$ a\FOOBAR=newvalue" -i samefile

Apakah ada cara untuk melakukan ini, yaitu. menjaga urutan garis, dalam satu baris sed? Jika beberapa utilitas lain (awk, ...) melakukan ini, saya akan mengambil alih.

Jawaban:


29

Ini sebenarnya cukup sederhana dengan sed: jika suatu baris cocok hanya menyalinnya ke hruang lama maka substitute nilainya.
Pada $baris pertama, xubah ruang tahan dan ruang pola, lalu periksa apakah ruang kosong. Jika tidak kosong, itu berarti penggantian sudah dilakukan jadi tidak ada yang bisa dilakukan. Jika kosong, itu berarti tidak ada kecocokan yang ditemukan jadi ganti ruang pola dengan variabel = nilai yang diinginkan kemudian tambahkan ke baris saat ini di buffer penahan. Akhirnya, xubah lagi:

sed '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile

Di atas adalah gnu sedsintaksis. Portabel:

sed '/^FOOBAR=/{
h
s/=.*/=newvalue/
}
${
x
/^$/{
s//FOOBAR=newvalue/
H
}
x
}' infile

1
Bagaimana ini terlihat jika newvaluedisimpan dalam variabel?
user1810087

sed "/^${varName}=/{h;s/=.*/=${varValue}/};\${x;/^$/{s//${varName}=${varValue}/;H};x}" ${VARFILE}dengan variabel, tanda kutip ganda untuk memungkinkan substitusi variabel dan dan keluar dari $yang bukan substitusi pada saat ini, juga jika nilai Anda disimpan di $$varName:sed "/^${varName}=/{h;s/=.*/=${!varName}/};\${x;/^$/{s//${varName}=${!varName}/;H};x}" ${VARFILE}
DarkMukke

1
Solusi ini bukan lol 'sederhana'. Tapi itu berhasil. sed halaman manual bukan hal termudah untuk disaring, akan sangat membantu jika Anda dapat merinci efek dari setiap bagian dari ekspresi :)
user2066480

18

Ini mungkin bisa dipersingkat. Ini bukan perintah sed tunggal dan ini juga menggunakan grep, tetapi ini pada dasarnya tampaknya apa yang Anda inginkan. Ini satu baris, dan itu mengedit file di tempat (tidak ada file temp).

grep -q "^FOOBAR=" file && sed "s/^FOOBAR=.*/FOOBAR=newvalue/" -i file || 
    sed "$ a\FOOBAR=newvalue" -i file

13

Ini adalah sedpendekatan yang lebih sederhana , karena saya merasa tidak sed hold spacemudah untuk dikerjakan. Jika Anda merasa nyaman hold space, menggunakan pendekatan don_crissti memberi peluang tambahan untuk mempertahankan apa pun dari baris yang ada, tetapi ini biasanya sangat jarang.

Dalam pendekatan ini, Anda cukup mencetak semua kecuali garis yang ingin Anda jatuhkan dan kemudian di akhir, tambahkan penggantian.

sed -n -e '/^FOOBAR=/!p' -e '$aFOOBAR=newvalue' infile

1
Versi ini sangat bagus jika Anda berencana untuk memperbarui file yang mungkin memiliki nilai yang sudah ada tetapi sudah usang.
guillem

3

Berdasarkan jawaban lain, jika apa yang ingin Anda lakukan adalah mengganti nilai variabel jika variabel itu ada di file dan menambahkannya ke akhir file jika tidak (yang tidak seddilakukan oleh perintah yang diposting ), Anda dapat mencoba ini:

perl -ne '$c=1 if s/^FOOBAR=.*$/FOOBAR=newvalue/;  
             print; 
             END{print "FOBAR=newvalue" unless $c==1}' file > tmpfile && 
mv tmpfile file

1

Ini sedikit lebih mudah dalam awk, meskipun "in-editing" tidak otomatis:

awk -v varname="FOOBAR" -v newval="newvalue" '
    BEGIN {FS = OFS = "="}
    $1 == varname {$2 = newval; found = 1}
    {print}
    END {if (! found) {print varname, newval}}
' file > tempfile &&
mv tempfile file

1

Cukup gunakan grepdan echountuk membuat catatan kosong:

grep -q '^FOOBAR=' somefile || echo 'FOOBAR=VALUE' >> somefile
sed -i 's/FOOBAR=.*$/FOOBAR=VALUE/' somefile 

Setiap baris lolos dengan kode kesalahan nol.


Anda kehilangan pemesanan baris sambil menambahkan perintah tambahan dan grepdanecho
BlakBat

Memang jika Anda memasukkan item baru di akhir file, tetapi tetap memesan jika Anda mengganti nilai lama.
MUY Belgium

0

Sebenarnya berfungsi dengan yang berikut: sed -i '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile Pada jawaban yang dipilih -i hilang.


3
Ini benar-benar komentar dan bukan jawaban untuk pertanyaan awal. Untuk mengkritik atau meminta klarifikasi dari penulis, tinggalkan komentar di bawah posting mereka - Anda selalu dapat mengomentari posting Anda sendiri, dan begitu Anda memiliki reputasi yang cukup, Anda akan dapat mengomentari setiap posting . Harap baca Mengapa saya perlu 50 reputasi untuk berkomentar? Apa yang bisa saya lakukan?
DavidPostill

-1

untuk melakukan ini dengan perl dan di tempat ini berfungsi dengan baik untuk saya:

grep ^FOOBAR= my.file && perl -i -ple "s/^FOOBAR=.+/FOOBAR=newvalue/g" my.file || echo FOOBAR=newvalue >> my.file

2
Jawaban ini buruk dalam banyak hal! grepdan echodan perlbukannya melakukan segala sesuatu dalam perl? Juga, menulis ke file ( >> my.file) saat Anda membacanya ( grep my.file)?
vladr
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.