web-dev-qa-db-de.com

So fügen Sie eine Zeile in sed hinzu, wenn keine Übereinstimmung gefunden wird

Ich verwende den folgenden sed-Befehl, um einige Parameter in einer Konfigurationsdatei zu ersetzen:

sed -i 's/^option.*/option=value/g' /etc/fdm_monitor.conf

Jetzt habe ich ein Problem. Wenn die Zeile nicht vorhanden ist, möchte ich sie am Ende der Datei hinzufügen.

Ich rufe das mit einer popen aus einem C-Programm auf. Ich habe versucht, awk zu verwenden.

30
Evilmachine

Versuche dies:

grep -q '^option' file && sed -i 's/^option.*/option=value/' file || echo 'option=value' >> file
57
kev

Mit sed die einfache Syntax:

sed \
    -e '/^\(option=\).*/{s//\1value/;:a;n;ba;q}' \
    -e '$aoption=value' filename

Dies würde den Parameter ersetzen, falls er existiert. Andernfalls wird er am Ende der Datei hinzugefügt.

Verwenden Sie die Option -i, wenn Sie die Datei direkt bearbeiten möchten.


Wenn Sie Leerzeichen akzeptieren und beibehalten und zusätzlich den Kommentar entfernen möchten, falls die Zeile bereits vorhanden ist, aber auskommentiert ist, schreiben Sie:

sed -i \
    -e '/^#\?\(\s*option\s*=\s*\).*/{s//\1value/;:a;n;ba;q}' \
    -e '$aoption=value' filename

Bitte beachten Sie, dass weder Option noch Wert einen Schrägstrich / enthalten dürfen, andernfalls müssen Sie ihn nach \/ umschreiben.


Um die bash-Variablen $option und $value zu verwenden, können Sie schreiben:

sed -i \
    -e '/^#\?\(\s*'${option//\//\\/}'\s*=\s*\).*/{s//\1'${value//\//\\/}'/;:a;n;ba;q}' \
    -e '$a'${option//\//\\/}'='${value//\//\\/} filename

Der Bash-Ausdruck ${option//\//\\/} zitiert Schrägstriche und ersetzt alle / durch \/.

Hinweis: Ich bin gerade in ein Problem geraten. In Bash können Sie "${option//\//\\/}" zitieren, aber in der sh von busybox funktioniert das nicht. Sie sollten also die Anführungszeichen vermeiden, zumindest in Nicht-Bourne-Shells.


Alles zusammen in einer Bash-Funktion:

# call option with parameters: $1=name $2=value $3=file
function option() {
    name=${1//\//\\/}
    value=${2//\//\\/}
    sed -i \
        -e '/^#\?\(\s*'"${name}"'\s*=\s*\).*/{s//\1'"${value}"'/;:a;n;ba;q}' \
        -e '$a'"${name}"'='"${value}" $3
}

Erklärung:

  • /^\(option=\).*/: Übereinstimmende Zeilen, die mit option= beginnen, und (.*) ignorieren alles nach dem =. Der \(\) schließt den Teil ein, den wir als \1later wiederverwenden werden.
  • /^#\?(\s*'"${option//////}"'\s*=\s*).*/: Ignorieren Sie auf Kommentar Code mit # an der der Zeile beginnen. \? bedeutet «optional». Der Kommentar wird entfernt, da er sich außerhalb des kopierten Teils in \(\) befindet. \s* bedeutet «beliebig viele Leerräume» (Leerzeichen, Tabulator). Leerzeichen werden kopiert, da sie sich innerhalb von \(\) befinden, so dass Sie die Formatierung nicht verlieren.
  • /^\(option=\).*/{…}: Wenn eine Zeile /…/ übereinstimmt, führen Sie den nächsten Befehl aus. Der auszuführende Befehl ist kein einzelner Befehl, sondern ein Block {…}.
  • s//…/: Suchen und Ersetzen. Da der Suchbegriff // leer ist, gilt er für die letzte Übereinstimmung, die /^\(option=\).*/ war.
  • s//\1value/: Replace the last match with everything in (…) , referenced by\1and the textvalue`
  • :a;n;ba;q: Beschriftung a setzen, dann nächste Zeile n lesen, dann b (oder goto) zurück zur Beschriftung a, also alle Zeilen bis und und Datei lesen, also nach der ersten Übereinstimmung alle folgenden Zeilen ohne weiteres abrufen wird bearbeitet. Dann q guit und ignoriere deshalb alles andere.
  • $aoption=value: Hängen Sie am Ende der Datei $a den Text option=value an.

Weitere Informationen zu sed und eine Befehlsübersicht finden Sie in meinem Blog:

8
Marc Wäckerlin

Hier ist ein 1-Liner-Sed, das die Arbeit inline erledigt. Beachten Sie, dass die Position der Variablen und ihre Einrückung in der Datei erhalten bleibt, wenn sie existiert. Dies ist häufig für den Kontext wichtig, z. B. wenn Kommentare vorhanden sind oder wenn sich die Variable in einem eingerückten Block befindet. Jede Lösung, die auf dem Paradigma "delete-then-append" basiert, schlägt dabei fehl.

   sed -i '/^[ \t]*option=/{h;s/=.*/=value/};${x;/^$/{s//option=value/;H};x}' test.conf

Mit einem generischen Paar von Variablen/Werten können Sie es folgendermaßen schreiben:

   var=c
   val='12 34' # it handles spaces nicely btw
   sed -i '/^[ \t]*'"$var"'=/{h;s/=.*/='"$val"'/};${x;/^$/{s//c='"$val"'/;H};x}' test.conf

Wenn Sie auch Inline-Kommentare beibehalten möchten, können Sie dies mit einer catch-Gruppe tun. Z.B. wenn test.conf folgendes enthält:

a=123
# Here is "c":
  c=999 # with its own comment and indent
b=234
d=567

Dann das laufen lassen

var='c'
val='"yay"'
sed -i '/^[ \t]*'"$var"'=/{h;s/=[^#]*\(.*\)/='"$val"'\1/;s/'"$val"'#/'"$val"' #/};${x;/^$/{s//'"$var"'='"$val"'/;H};x}' test.conf

Produziert das:

a=123
# Here is "c":
  c="yay" # with its own comment and indent
b=234
d=567
4
MoonCactus

Mit sed könnte man sagen:

sed -e '/option=/{s/.*/option=value/;:a;n;:ba;q}' -e 'aoption=value' filename

Dies würde den Parameter ersetzen, falls er existiert. Andernfalls wird er am Ende der Datei hinzugefügt.


Verwenden Sie die Option -i, wenn Sie die Datei direkt bearbeiten möchten:

sed -i -e '/option=/{s/.*/option=value/;:a;n;:ba;q}' -e 'aoption=value' filename
3
devnull

Als One-Liner nur für Axt:

awk -v s=option=value '/^option=/{$0=s;f=1} {a[++n]=$0} END{if(!f)a[++n]=s;for(i=1;i<=n;i++)print a[i]>ARGV[1]}' file

ARGV [1] ist Ihre Eingabe file. Sie wird in der for-Schleife desEND-Blocks geöffnet und beschrieben. Das Öffnen von file für die Ausgabe im END-Block ersetzt die Notwendigkeit von Dienstprogrammen wie sponge oder das Schreiben in eine temporäre Datei und das anschließende mving der temporären Datei in file.

Die beiden Zuordnungen zum Array a[] sammeln alle Ausgabezeilen in a. if(!f)a[++n]=s hängt den neuen option=value an, wenn die awk-Hauptschleife option in file nicht finden konnte. 

Ich habe ein paar Leerzeichen (nicht viele) für die Lesbarkeit hinzugefügt, aber Sie benötigen wirklich nur ein Leerzeichen im gesamten awk-Programm, das Leerzeichen nach print. Wenn file# comments enthält, bleiben sie erhalten.

3
stepse

hier ist ein awk one-liner:

 awk -v s="option=value" '/^option/{f=1;$0=s}7;END{if(!f)print s}' file

die Datei wird nicht direkt geändert. Sie können jedoch Folgendes tun:

awk '...' file > tmpfile && mv tmpfile file
2
Kent

Hier ist eine awk-Implementierung

/^option *=/ { 
  print "option=value"; # print this instead of the original line
  done=1;               # set a flag, that the line was found
  next                  # all done for this line
}
{print}                 # all other lines -> print them
END {                   # end of file
  if(done != 1)         # haven't found /option=/ -> add it at the end of output
    print "option=value"
}

Führen Sie es mit aus

awk -f update.awk < /etc/fdm_monitor.conf > /etc/fdm_monitor.conf.tmp && \
   mv /etc/fdm_monitor.conf.tmp /etc/fdm_monitor.conf

oder 

awk -f update.awk < /etc/fdm_monitor.conf | sponge /etc/fdm_monitor.conf

EDIT: Als Einzeiler: 

awk '/^option *=/ {print "option=value";d=1;next}{print}END{if(d!=1)print "option=value"}' /etc/fdm_monitor.conf | sponge /etc/fdm_monitor.conf
2
rzymek
 sed -i 's/^option.*/option=value/g' /etc/fdm_monitor.conf
 grep -q "option=value" /etc/fdm_monitor.conf || echo "option=value" >> /etc/fdm_monitor.conf
2
Jack
sed -i '1 h
1 !H
$ {
   x
   s/^option.*/option=value/g
   t
   s/$/\
option=value/
   }' /etc/fdm_monitor.conf

Laden Sie die gesamte Datei in den Puffer, ändern Sie am Ende alle Vorkommnisse und fügen Sie am Ende keine Änderungen hinzu

1
NeronLeVelu

Ich habe mich mit der grep/sed-Lösung von kev beschäftigt, indem ich Variablen gesetzt habe, um Doppelarbeit zu reduzieren.

Stellen Sie die Variablen in der ersten Zeile ein (Hinweis: $_option muss mit allem in der Zeile übereinstimmen, bis der Wert [einschließlich aller Trennzeichen wie = oder:]) angezeigt wird.

_ file = "/ etc/ssmtp/ssmtp.conf" _option = "mailhub =" _value = "my.domain.tld"\
 sh -c '\ 
 grep -q "^ $ _ Option" "$ _file"\
 && sed -i "s/^ $ _ -Option. */$ _ -Option $ _value /" "$ _file"\
 || echo "$ _option $ _value" >> "$ _file"\
 '

Beachten Sie, dass der sh -c '...' nur dazu führt, dass der Bereich der Variablen erweitert wird, ohne dass eine export erforderlich ist. (Siehe Setzen einer Umgebungsvariablen vor einem Befehl in bash, der für den zweiten Befehl in einer Pipe nicht funktioniert )

0
Jonas Eberle

Mit dieser Funktion können Sie Konfigurationsänderungen suchen und suchen:

#!/bin/bash

#Find and Replace config values
find_and_replace_config () {
   file=$1
   var=$2
   new_value=$3
   awk -v var="$var" -v new_val="$new_value" 'BEGIN{FS=OFS="="}match($1, "^\\s*" var "\\s*") {$2=" " new_val}1' "$file" > output.tmp && Sudo mv output.tmp $file
}

find_and_replace_config /etc/php5/Apache2/php.ini max_execution_time 60
0
Guray Celik