Anhängen einer Zeile an eine Datei nur, wenn sie noch nicht vorhanden ist

157

Ich muss die folgende Zeile am Ende einer Konfigurationsdatei hinzufügen:

include "/configs/projectname.conf"

zu einer Datei namens lighttpd.conf

Ich sedmöchte dies verwenden, kann aber nicht herausfinden, wie.

Wie würde ich es nur einfügen, wenn die Zeile noch nicht existiert?

Benjamin Dell
quelle
Wenn Sie versuchen, eine INI-Datei zu bearbeiten, ist das Tool crudinimöglicherweise eine gute Option (aber noch nicht für lighthttpd)
rubo77

Antworten:

289

Halte es einfach :)

grep + echo sollte ausreichen:

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

Bearbeiten: @cerin und @ thijs-wouters Vorschläge aufgenommen .

drAlberT
quelle
2
Ich würde den Schalter -q zu grep hinzufügen, um die Ausgabe zu unterdrücken: grep -vq ...
Dave Kirby
1
Zu Ihrer Information, mit -v und && scheint der Trick nicht zu funktionieren, wohingegen || ohne -v funktioniert.
bPizzi
3
Dies funktioniert nur für sehr einfache Zeilen. Andernfalls interpretiert grep die meisten Nicht-Alpha-Zeichen in Ihrer Zeile als Muster, sodass die Zeile in der Datei nicht erkannt wird.
Cerin
2
Schöne Lösung. Dies funktioniert natürlich auch zum Auslösen komplizierterer Ausdrücke. Meins verwendet das echo, um einen catmehrzeiligen Heredoc in eine Konfigurationsdatei auszulösen .
Eric L.
2
Hinzufügen -s, um Fehler zu ignorieren, wenn die Datei nicht vorhanden ist, und eine neue Datei mit nur dieser Zeile erstellen.
Frank
78

Dies wäre eine saubere, gut lesbare und wiederverwendbare Lösung mit grepund echoeine Zeile in eine Datei nur hinzufügen , wenn es nicht bereits vorhanden ist :

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

Wenn Sie die gesamte Linie anpassen müssen, verwenden Sie grep -xqF

Hinzufügen -s, um Fehler zu ignorieren, wenn die Datei nicht vorhanden ist, und eine neue Datei mit nur dieser Zeile erstellen.

rubo77
quelle
5
Wenn es sich um eine Datei handelt, für die Sie Root-Berechtigungen benötigen:sudo grep -qF "$LINE" "$FILE" || echo "$LINE" | sudo tee -a "$FILE"
Nebffa
Dies funktioniert tatsächlich nicht, wenn die Zeile mit a beginnt -.
Hyperknot
@zsero: guter Punkt! Ich habe --den Befehl grep hinzugefügt , damit eine mit einem Bindestrich beginnende $ LINE nicht mehr als Optionen interpretiert wird.
Rubo77
13

Hier ist eine sedVersion:

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

Wenn sich Ihre Zeichenfolge in einer Variablen befindet:

string='include "/configs/projectname.conf"'
sed -e "\|$string|h; \${x;s|$string||;{g;t};a\\" -e "$string" -e "}" file
Bis auf weiteres angehalten.
quelle
Für einen Befehl, der eine Teilzeichenfolge durch den vollständigen / aktualisierten Konfigurationsdateieintrag ersetzt oder die Zeile nach Bedarf 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
anfügt
Schön, es hat mir sehr geholfen +1. Könnten Sie bitte Ihren sed Ausdruck erklären?
Rahul
Ich würde gerne eine kommentierte Erklärung dieser Lösung sehen. Ich habe sedjahrelang aber meistens nur den sBefehl benutzt. Die holdund patternSpace Swaps sind mir ein Rätsel.
am
11

Wenn Sie in eine geschützte Datei schreiben, funktionieren die Antworten von @drAlberT und @ rubo77 möglicherweise nicht für Sie, da man nicht sudo kann >>. Eine ähnlich einfache Lösung wäre dann die Verwendung tee --append(oder unter MacOS tee -a):

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

Wenn ein Tag jemand anderes hat mit diesem Code als „Legacy - Code“ beschäftigen, dann wird dieser Person dankbar sein , wenn Sie einen weniger exoteric Code schreiben, wie

grep -q -F 'include "/configs/projectname.conf"' lighttpd.conf
if [ $? -ne 0 ]; then
  echo 'include "/configs/projectname.conf"' >> lighttpd.conf
fi
Marcelo Ventura
quelle
1
Entschuldigung, wenn Sie Shell nicht kennen, gehen Sie und verwalten Sie Windows. Die Lösungen mit && und || sind normale Shell-Syntax und sollten von jedem kompetenten Administrator verstanden werden. Es gibt dort überhaupt nichts Esoterisches.
Graham Nicholls
3
Danke für deinen Kommentar Graham! Ja, es ist normale Shell-Syntax. Und ja, jeder kompetente Administrator sollte es verstehen. Es funktioniert jedoch als solches basierend auf einem Nebeneffekt des Ausdrucksbewertungsalgorithmus innerhalb der Shell. Sie können also alles so nennen, wie Sie möchten, außer unkompliziert - das war mein ursprünglicher Punkt.
Marcelo Ventura
6

Eine andere Lösung besteht darin, sie immer an die letzte Zeile anzuhängen und eine bereits vorhandene zu löschen.

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

"ordnungsgemäß maskiert" bedeutet, einen Regex einzufügen, der Ihrem Eintrag entspricht, dh alle Regex-Steuerelemente aus Ihrem tatsächlichen Eintrag zu entfernen, dh einen Backslash vor ^ $ / *? + () zu setzen.

Dies könnte in der letzten Zeile Ihrer Datei fehlschlagen, oder wenn es keine baumelnde neue Zeile gibt, bin ich mir nicht sicher, aber das könnte durch eine raffinierte Verzweigung behoben werden ...

Robin479
quelle
1
Schöner Einzeiler, kurz und leicht zu lesen. Funktioniert hervorragend zum Aktualisieren von etc / hosts:sed -i.bak -e '$a\' -e "$NEW_IP\t\t$HOST.domain\t$HOST" -e "/.*$HOST/d" /etc/hosts
Noam Manos
Eine kleinere Version:sed -i -e '/<entry>/d; $a <entry>'
Jérôme Pouiller
@Jezz Ich denke, dies wird fehlschlagen, wenn sich der Eintrag bereits am Ende der Datei befindet, da der dBefehl das sed-Programm neu startet und dadurch das append überspringt , wenn sich das Löschen zufällig in der letzten Zeile befindet.
Robin479
@ Robin479 Verdammt, append müssen vor delete ausgeführt werden : sed -i -e '$a<entry>' -e '/<entry>/d'. Es ist fast das gleiche wie Ihre ursprüngliche Antwort.
Jérôme Pouiller
3

benutze awk

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file file
Ghostdog74
quelle
Ist es 'Datei Datei' oder nur 'Datei'?
Webdevguy
Dies liegt file filedaran, dass zwei Durchgänge über dieselbe Datei ausgeführt werden. NR==FNRist wahr beim ersten Durchgang, aber nicht beim zweiten. Dies ist eine verbreitete Awk-Sprache.
Tripleee
1

Die Antworten mit grep sind falsch. Sie müssen eine Option -x hinzufügen, um mit der gesamten Zeile übereinzustimmen. Andernfalls #text to addstimmen Zeilen wie diese immer noch überein, wenn Sie genau hinzufügen möchten text to add.

Die richtige Lösung ist also so etwas wie:

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

Verwenden von sed: Wird am Ende der Zeile eingefügt. Sie können natürlich auch wie gewohnt Variablen übergeben.

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

Mit Oneliner sed

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

Die Verwendung von Echo funktioniert möglicherweise nicht unter root, funktioniert jedoch so. Sie können die Dinge jedoch nicht automatisieren, wenn Sie dies tun möchten, da möglicherweise nach einem Kennwort gefragt wird.

Ich hatte ein Problem, als ich versuchte, für einen bestimmten Benutzer vom Stamm aus zu bearbeiten. Nur das Vorherige hinzuzufügen $usernamewar eine Lösung für mich.

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
quelle
Willkommen bei stackoverflow! Bitte lesen Sie die Frage sorgfältig durch, bevor Sie eine Antwort veröffentlichen - das OP fragte, wie mit sed
eingefügt werden soll
-1

Ich musste eine Datei mit eingeschränkten Schreibberechtigungen bearbeiten sudo. Arbeiten mit der Antwort von ghostdog74 und Verwenden einer temporären Datei:

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
quelle
Dies sieht nicht richtig aus, es gehen die anderen Zeilen aus der Datei verloren, wenn die Konfigurationszeile fehlt.
Tripleee