Menambahkan baris ke file hanya jika belum ada

157

Saya perlu menambahkan baris berikut ke akhir file konfigurasi:

include "/configs/projectname.conf"

ke file yang disebut lighttpd.conf

Saya melihat kegunaan seduntuk melakukan ini, tetapi saya tidak tahu caranya.

Bagaimana saya hanya memasukkannya jika salurannya belum ada?

Benjamin Dell
sumber
Jika Anda mencoba mengedit file-in, alat ini crudinimungkin merupakan opsi yang baik (tetapi belum untuk lighthttpd)
rubo77

Jawaban:

292

Sederhana saja :)

grep + echo sudah cukup:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar

Edit: memasukkan @cerin dan saran @ thijs-wouters .

drAlberT
sumber
2
Saya akan menambahkan -q beralih ke grep untuk menekan output: grep -vq ...
Dave Kirby
1
FYI, menggunakan -v dan && tidak terlihat berhasil, sedangkan || tanpa -v bekerja.
bPizzi
3
Ini hanya berfungsi untuk garis yang sangat sederhana. Jika tidak, grep menginterpretasikan sebagian besar karakter non-alfa di baris Anda sebagai pola, menyebabkannya tidak mendeteksi baris dalam file.
Cerin
2
Solusi indah. Ini juga berfungsi untuk memicu ekspresi yang lebih rumit, tentu saja. Milik saya menggunakan echountuk memicu catheredoc multiline ke file konfigurasi.
Eric L.
2
Tambah -suntuk mengabaikan kesalahan ketika file tidak ada, membuat file baru hanya dengan baris itu.
Frank
78

Ini akan menjadi solusi yang bersih, mudah dibaca dan dapat digunakan kembali menggunakan grepdan echomenambahkan baris ke file hanya jika belum ada:

LINE='include "/configs/projectname.conf"'
FILE='lighttpd.conf'
grep -qF -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"

Jika Anda harus mencocokkan penggunaan seluruh baris grep -xqF

Tambah -suntuk mengabaikan kesalahan ketika file tidak ada, membuat file baru hanya dengan baris itu.

rubo77
sumber
5
Jika itu ke file di mana Anda memerlukan izin root:sudo grep -qF "$LINE" "$FILE" || echo "$LINE" | sudo tee -a "$FILE"
nebffa
Ini sebenarnya tidak berfungsi ketika baris dimulai dengan a -.
hyperknot
@ zsero: poin bagus! Saya menambahkan --dalam perintah grep, jadi itu tidak akan menafsirkan $ LINE dimulai dengan tanda hubung sebagai opsi lagi.
rubo77
13

Ini sebuah sedversi:

sed -e '\|include "/configs/projectname.conf"|h; ${x;s/incl//;{g;t};a\' -e 'include "/configs/projectname.conf"' -e '}' file

Jika string Anda dalam variabel:

string='include "/configs/projectname.conf"'
sed -e "\|$string|h; \${x;s|$string||;{g;t};a\\" -e "$string" -e "}" file
Dijeda sampai pemberitahuan lebih lanjut.
sumber
Untuk perintah yang menggantikan sebagian string dengan entri file konfigurasi lengkap / yang diperbarui, atau menambahkan baris sesuai kebutuhan:sed -i -e '\|session.*pam_mkhomedir.so|h; ${x;s/mkhomedir//;{g;tF};a\' -e 'session\trequired\tpam_mkhomedir.so umask=0022' -e '};:F;s/.*mkhomedir.*/session\trequired\tpam_mkhomedir.so umask=0022/g;' /etc/pam.d/common-session
bgStack15
Bagus, itu banyak membantu saya memberi +1. bisa tolong jelaskan ekspresimu?
Rahul
Saya ingin melihat penjelasan solusi ini yang beranotasi. Saya sudah menggunakan sedselama bertahun-tahun tetapi kebanyakan hanya sperintah. The holddan patternswap space yang di luar saya.
nortally
11

Jika menulis ke file yang dilindungi, jawaban @drAlberT dan @ rubo77 mungkin tidak berfungsi untuk Anda karena orang tidak dapat melakukan sudo >>. Sebuah solusi sama sederhana , maka, akan menggunakan tee --append(atau, pada MacOS, tee -a):

LINE='include "/configs/projectname.conf"'
FILE=lighttpd.conf
grep -qF "$LINE" "$FILE"  || echo "$LINE" | sudo tee --append "$FILE"
hamx0r
sumber
6

Jika, suatu hari, orang lain harus berurusan dengan kode ini sebagai "kode warisan", maka orang itu akan berterima kasih jika Anda menulis kode yang kurang eksoteris, seperti

grep -q -F 'include "/configs/projectname.conf"' lighttpd.conf
if [ $? -ne 0 ]; then
  echo 'include "/configs/projectname.conf"' >> lighttpd.conf
fi
Marcelo Ventura
sumber
1
Maaf, jika Anda tidak tahu shell, maka pergi dan kelola Windows. Solusi menggunakan && dan || adalah sintaksis shell normal, dan harus dipahami oleh admin yang kompeten. Tidak ada yang esoteris di sana sama sekali.
Graham Nicholls
3
Terima kasih atas komentar Anda, Graham! Ya, itu adalah sintaksis shell normal. Dan ya, setiap admin yang kompeten harus memahaminya. Namun, ia bekerja berdasarkan efek samping dari algoritma evaluasi ekspresi di dalam shell. Jadi Anda bisa menyebutnya sesuka Anda, kecuali langsung - yang merupakan poin awal saya.
Marcelo Ventura
6

solusi sed lain adalah untuk selalu menambahkannya di baris terakhir dan menghapus yang sudah ada sebelumnya.

sed -e '$a\' -e '<your-entry>' -e "/<your-entry-properly-escaped>/d"

"lolos dengan benar" berarti menempatkan regex yang cocok dengan entri Anda, yaitu untuk menghindari semua kontrol regex dari entri Anda yang sebenarnya, yaitu untuk meletakkan backslash di depan ^ $ / *? + ().

ini mungkin gagal pada baris terakhir file Anda atau jika tidak ada baris baru yang menggantung, saya tidak yakin, tapi itu bisa ditangani oleh beberapa percabangan yang bagus ...

Robin479
sumber
1
Bagus satu garis, pendek dan mudah dibaca. Bekerja sangat baik untuk memperbarui dll / host:sed -i.bak -e '$a\' -e "$NEW_IP\t\t$HOST.domain\t$HOST" -e "/.*$HOST/d" /etc/hosts
Noam Manos
Versi yang lebih kecil:sed -i -e '/<entry>/d; $a <entry>'
Jérôme Pouiller
@ Jezz Saya pikir ini akan gagal, jika entri sudah ada di akhir file, karena dperintah akan me-restart program sed dan dengan demikian melewatkan append, jika penghapusan kebetulan berada di baris terakhir.
Robin479
@ Robin479 Damned, append harus dilaksanakan sebelum dapus: sed -i -e '$a<entry>' -e '/<entry>/d'. Itu hampir sama dengan jawaban awal Anda.
Jérôme Pouiller
3

gunakan awk

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file file
ghostdog74
sumber
Apakah ini 'file file' atau hanya 'file'?
webdevguy
Itu file filekarena membuat dua melewati file yang sama. NR==FNRbenar pada pass pertama, tetapi tidak pada pass kedua. Ini adalah ungkapan awk yang umum.
tripleee
1

Jawaban menggunakan grep salah. Anda perlu menambahkan opsi -x untuk mencocokkan seluruh baris jika tidak, baris seperti #text to addmasih akan cocok ketika ingin menambahkan dengan tepat text to add.

Jadi solusi yang benar adalah sesuatu seperti:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar
Thijs Wouters
sumber
1

Menggunakan sed: Ini akan menyisipkan di akhir baris. Anda juga dapat mengirimkan variabel seperti biasa.

grep -qxF "port=9033" $light.conf
if [ $? -ne 0 ]; then
  sed -i "$ a port=9033" $light.conf
else
    echo "port=9033 already added"
fi

Menggunakan oneliner sed

grep -qxF "port=9033" $lightconf || sed -i "$ a port=9033" $lightconf

Menggunakan gema mungkin tidak berfungsi di bawah root, tetapi akan berfungsi seperti ini. Tapi itu tidak akan membiarkan Anda mengotomatiskan hal-hal jika Anda ingin melakukannya karena mungkin meminta kata sandi.

Saya punya masalah ketika saya mencoba mengedit dari root untuk pengguna tertentu. Hanya menambahkan $usernamesebelumnya adalah perbaikan untuk saya.

grep -qxF "port=9033" light.conf
if [ $? -ne 0 ]; then
  sudo -u $user_name echo "port=9033" >> light.conf
else
    echo "already there"    
fi
Rakib Fiha
sumber
Selamat datang di stackoverflow! Silakan baca pertanyaan dengan seksama sebelum mengirim jawaban - OP bertanya bagaimana memasukkan menggunakan sed
Markoorn
-1

Saya perlu mengedit file dengan izin tulis terbatas sehingga diperlukan sudo. bekerja dari jawaban ghostdog74 dan menggunakan file temp:

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file > /tmp/file
sudo mv /tmp/file file
webdevguy
sumber
Ini tidak terlihat benar, itu akan kehilangan baris lain dari file ketika baris konfigurasi hilang.
tripleee