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.

BlakBat
sumber

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
don_crissti
sumber
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
snapshoe
sumber
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
haridsv
sumber
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
terdon
sumber
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
glenn jackman
sumber
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.

MUY Belgia
sumber
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.

Yogesh Kamat
sumber
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
antweis
sumber
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