Aggiunta di una riga a un file solo se non esiste già


157

Devo aggiungere la seguente riga alla fine di un file di configurazione:

include "/configs/projectname.conf"

in un file chiamato lighttpd.conf

Sto cercando di utilizzare sedper fare questo, ma non riesco a capire come.

Come lo inserirò solo se la linea non esiste già?


Se stai cercando di modificare un file ini lo strumento crudinipotrebbe essere una buona opzione (ma non ancora per lighthttpd)
rubo77

Risposte:


292

Mantienilo semplice :)

grep + echo dovrebbe essere sufficiente:

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

Modifica: incorporati suggerimenti di @cerin e @ thijs-wouters .


2
Aggiungerei l'opzione -q a grep per sopprimere l'output: grep -vq ...
Dave Kirby,

1
Cordiali saluti, usando -v e && non sembra fare il trucco, mentre || senza -v funziona.
bPizzi,

3
Funziona solo con linee molto semplici. Altrimenti, grep interpreta la maggior parte dei caratteri non alfa nella linea come motivi, impedendo che rilevi la linea nel file.
Cerin,

2
Bella soluzione Questo funziona anche per innescare espressioni più complicate, ovviamente. Il mio usa il echoper innescare una cateredità multilinea in un file di configurazione.
Eric L.

2
Aggiungi -sper ignorare gli errori quando il file non esiste, creando un nuovo file con solo quella riga.
Frank

78

Questa sarebbe una soluzione pulita, leggibile e riutilizzabile utilizzando grepe echoper aggiungere una riga a un file solo se non esiste già:

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

Se è necessario abbinare l'intera riga, utilizzare grep -xqF

Aggiungi -sper ignorare gli errori quando il file non esiste, creando un nuovo file con solo quella riga.


5
Se si tratta di un file in cui sono necessari i permessi di root:sudo grep -qF "$LINE" "$FILE" || echo "$LINE" | sudo tee -a "$FILE"
nebffa,

Questo in realtà non funziona quando la linea inizia con a -.
hyperknot,

@zsero: buon punto! Ho aggiunto --il comando grep, quindi non interpreterà più una $ LINE che inizia con un trattino come opzioni.
rubo77,

13

Ecco una sedversione:

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

Se la stringa è in una variabile:

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

Per un comando che sostituisce una stringa parziale con la voce del file di configurazione completa / aggiornati, o aggiunge la linea in base alle esigenze: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

Bello, mi ha aiutato molto +1. potresti spiegare la tua espressione sed?
Rahul,

Mi piacerebbe vedere una spiegazione annotata di questa soluzione. Ho usato sedper anni ma principalmente solo il scomando. Le holde patternswap spazio sono al di là di me.
Nord

11

Se si scrive su un file protetto, le risposte di @drAlberT e @ rubo77 potrebbero non funzionare in quanto non è possibile eseguire sudo >>. Una soluzione altrettanto semplice , quindi, sarebbe quella di utilizzare tee --append(o, su MacOS tee -a):

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

6

Se, un giorno, qualcun altro deve trattare questo codice come "codice legacy", quella persona ti sarà grata se scrivi un codice meno exoterico, come

grep -q -F 'include "/configs/projectname.conf"' lighttpd.conf
if [ $? -ne 0 ]; then
  echo 'include "/configs/projectname.conf"' >> lighttpd.conf
fi

1
Spiacenti, se non conosci la shell, vai e gestisci Windows. Le soluzioni che utilizzano && e || sono normali sintassi della shell e dovrebbero essere compresi da qualsiasi amministratore competente. Non c'è niente di esoterico lì.
Graham Nicholls,

3
Grazie per il tuo commento Graham! Sì, è normale sintassi della shell. E sì, qualsiasi amministratore competente dovrebbe capirlo. Tuttavia, funziona come tale in base a un effetto collaterale dell'algoritmo di valutazione dell'espressione all'interno della shell. Quindi puoi chiamarlo come preferisci, tranne che per il semplice, che era il mio punto originale.
Marcelo Ventura,


6

un'altra soluzione sed è aggiungerla sempre sull'ultima riga ed eliminarne una preesistente.

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

"correttamente sfuggito" significa mettere una regex che corrisponde alla tua voce, cioè sfuggire a tutti i controlli regex dalla tua voce reale, cioè mettere una barra rovesciata di fronte a ^ $ / *? + ().

questo potrebbe fallire sull'ultima riga del tuo file o se non c'è newline penzolante, non ne sono sicuro, ma potrebbe essere risolto da qualche ramificazione intelligente ...


1
Simpatico one-liner, corto e di facile lettura. Funziona alla grande per l'aggiornamento etc / hosts:sed -i.bak -e '$a\' -e "$NEW_IP\t\t$HOST.domain\t$HOST" -e "/.*$HOST/d" /etc/hosts
Noam Manos

Una versione più piccola:sed -i -e '/<entry>/d; $a <entry>'
Jérôme Pouiller

@Jezz Penso che questo fallirà, se la voce è già alla fine del file, perché il dcomando riavvia il programma sed e quindi salta la append, se l'eliminazione è avvenuta sull'ultima riga.
Robin479,

@ Robin479 Damned, append deve essere eseguito prima delete: sed -i -e '$a<entry>' -e '/<entry>/d'. È quasi lo stesso della tua risposta originale.
Jérôme Pouiller,

3

usa awk

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file file

È 'file file' o solo 'file'?
webdevguy,

È file fileperché effettua due passaggi sullo stesso file. NR==FNRè vero al primo passaggio, ma non al secondo. Questo è un linguaggio Awk comune.
Tripleee

1

Le risposte usando grep sono sbagliate. È necessario aggiungere un'opzione -x per abbinare l'intera linea, altrimenti le linee simili #text to addcorrisponderanno comunque quando si cerca di aggiungere esattamente text to add.

Quindi la soluzione corretta è qualcosa di simile:

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

1

Usando sed: verrà inserito alla fine della riga. Naturalmente puoi anche passare le variabili come al solito.

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

Usando oneliner sed

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

L'uso dell'eco potrebbe non funzionare sotto root, ma funzionerà in questo modo. Ma non ti permetterà di automatizzare le cose se stai cercando di farlo dal momento che potrebbe richiedere la password.

Ho avuto un problema quando stavo provando a modificare dalla radice per un particolare utente. L'aggiunta del $usernameprecedente è stata una soluzione per me.

grep -qxF "port=9033" light.conf
if [ $? -ne 0 ]; then
  sudo -u $user_name echo "port=9033" >> light.conf
else
    echo "already there"    
fi

Benvenuto in StackOverflow! Si prega di leggere attentamente la domanda prima di inviare una risposta - l'OP ha chiesto come inserire utilizzando sed
Markoorn,

-1

Avevo bisogno di modificare un file con autorizzazioni di scrittura limitate, quindi necessario sudo. lavorando dalla risposta di ghostdog74 e usando un file temporaneo:

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

Questo non sembra corretto, perderà le altre righe del file quando manca la riga di configurazione.
Tripleee
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.