Een regel alleen aan een bestand toevoegen als deze nog niet bestaat

Ik moet de volgende regel toevoegen aan het einde van een configuratiebestand:

include "/configs/projectname.conf"

naar een bestand met de naam lighttpd.conf

Ik ben van plan om sedte gebruiken om dit te doen, maar ik weet niet hoe.

Hoe kan ik het alleen invoegen als de regel nog niet bestaat?


Antwoord 1, autoriteit 100%

Houd het simpel 🙂

grep+ echozou moeten volstaan:

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

Bewerken:
suggesties van @cerin en @thijs-wouters
verwerkt.


Antwoord 2, autoriteit 26%

Probeer dit:

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

Antwoord 3, autoriteit 25%

Dit zou een schone, leesbare en herbruikbare oplossing zijn die grepen echogebruikt om alleen een regel aan een bestand toe te voegen als deze nog niet bestaat:

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

Als je de hele regel wilt matchen, gebruik dan grep -xqF

Voeg -stoe om fouten te negeren wanneer het bestand niet bestaat, en maak een nieuw bestand met alleen die regel.


Antwoord 4, autoriteit 8%

Sed gebruiken, de eenvoudigste syntaxis:

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

Dit zou de parameter vervangen als deze bestaat, anders zou deze onderaan het bestand worden toegevoegd.

Gebruik de optie -ials u het bestand ter plekke wilt bewerken.


Als je spaties wilt accepteren en behouden, en als aanvulling op het verwijderen van de opmerking, als de regel al bestaat, maar is uitgecommentarieerd, schrijf dan:

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

Houd er rekening mee dat noch optie noch waarde een schuine streep /mag bevatten, anders moet u deze escapen naar \/.


Als je bash-variabelen $optionen $valuewilt gebruiken, kun je schrijven:

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

De bash-uitdrukking ${option//\//\\/}citeert schuine strepen, het vervangt alle /door \/.

Opmerking:Gewoon vastgelopen in een probleem. In bash mag je "${option//\//\\/}"citeren, maar in de sh van busybox werkt dit niet, dus je moet de aanhalingstekens vermijden, tenminste in niet -bourne-shells.


Alles gecombineerd in een bash-functie:

# 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
}

Uitleg:

  • /^\(option=\).*/: Match regels die beginnen met option=en (.*) negeer alles na de =. De \(…\)omsluit het deel dat we later zullen hergebruiken als \1.
  • /^#?(\s*'”${option//////}”‘\s*=\s*).*/: Negeer de uitgeschreven code met #aan het begin van de regel. \?betekent «optioneel». De opmerking wordt verwijderd, omdat deze buiten het gekopieerde deel in \(…\)valt. \s*betekent «een willekeurig aantal spaties» (spatie, tabulator). Spaties worden gekopieerd, aangezien ze zich binnen \(…\)bevinden, zodat u de opmaak niet verliest.
  • /^\(option=\).*/{…}: Als het overeenkomt met een regel /…/, voer dan het volgende commando uit. Uit te voeren commando is geen enkel commando, maar een blok {…}.
  • s//…/: zoeken en vervangen. Aangezien de zoekterm leeg is //, is deze van toepassing op de laatste overeenkomst, namelijk /^\(option=\).*/.
  • s//\1value/: Replace the last match with everything in (…), referenced by \1and the textwaarde `
  • :a;n;ba;q: stel het label ain, lees dan de volgende regel nen vertakking b(of ga naar) terug naar label a, dat wil zeggen: lees alle regels tot het einde van het bestand, dus na de eerste match haal je gewoon alle volgende regels op zonder verdere verwerking. Dan stopt qen negeert daarom al het andere.
  • $aoption=value: voeg aan het einde van bestand $ade tekst option=valuetoe

Meer informatie over seden een overzicht van opdrachten vind je op mijn blog:


Antwoord 5, autoriteit 4%

Als u naar een beveiligd bestand schrijft, werken de antwoorden van @drAlberT en @rubo77 mogelijk niet voor u, aangezien u >>niet kunt sudo. Een evenzo eenvoudige oplossing, dan , zou zijn om tee --appendte gebruiken (of, op MacOS, tee -a):

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

Antwoord 6, autoriteit 4%

Hier is een sed-versie:

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

Als je string in een variabele staat:

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

Antwoord 7, autoriteit 3%

Als op een dag iemand anders moetmet deze code omgaan als “legacy code”, dan zal die persoon je dankbaar zijn als je een minder exoterische code schrijft, zoals

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

Antwoord 8, autoriteit 2%

een andere sed-oplossing is om het altijd op de laatste regel toe te voegen en een reeds bestaande te verwijderen.

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

“goed-escaped” betekent dat u een regex plaatst die overeenkomt met uw invoer, d.w.z. alle regex-besturingselementen van uw daadwerkelijke invoer ontwijken, d.w.z. een backslash voor ^$/*?+() plaatsen.

dit kan mislukken op de laatste regel van je bestand of als er geen bungelende nieuwe regel is, weet ik niet zeker, maar dat kan worden opgelost door een handige vertakking…


Antwoord 9, autoriteit 2%

Hier is een one-liner sed die het werk inline doet. Merk op dat het behoudt de locatie van de variabele en zijn inspringingin het bestand als het bestaat. Dit is vaak belangrijk voor de context, zoals wanneer er opmerkingen in de buurt zijn of wanneer de variabele in een ingesprongen blok staat. Elke oplossing op basis van het paradigma “verwijderen-dan-toevoegen” faalt hierin slecht.

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

Met een generiek paar variabele/waarde kun je het op deze manier schrijven:

  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

Ten slotte, als je ook inline reacties wilt behouden, kun je dat doen met een catch-groep. bijv. als test.confhet volgende bevat:

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

Vervolgens uitvoeren

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

Produceert dat:

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

Antwoord 10

Als een onhandige oneliner:

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] is uw invoer file. Het wordt geopend en geschreven in de for-lus van het END-blok. Het openen van filevoor uitvoer in het END-blok vervangt de noodzaak voor hulpprogramma’s zoals spongeof het schrijven naar een tijdelijk bestand en vervolgens mvhet tijdelijke bestand omzetten in file.

De twee toewijzingen aan array a[]verzamelen alle uitvoerregels in a. if(!f)a[++n]=svoegt de nieuwe option=valuetoe als de hoofd awk-lus optionniet kon vinden in file.

Ik heb enkele spaties (niet veel) toegevoegd voor de leesbaarheid, maar je hebt eigenlijk maar Ă©Ă©n spatie nodig in het hele awk-programma, de spatie na print.
Als file# commentsbevat, worden deze bewaard.


Antwoord 11

gebruik awk

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

Antwoord 12

Hier is een awkimplementatie

/^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"
}

Voer het uit met

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

of

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

BEWERKEN:
Als oneliner:

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

Antwoord 13

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

Antwoord 14

hier is een rare one-liner:

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

dit brengt geen interne wijziging aan in het bestand, u kunt echter:

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

Antwoord 15

Met sedzou je kunnen zeggen:

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

Dit zou de parameter vervangen als deze bestaat, anders zou deze onderaan het bestand worden toegevoegd.


Gebruik de optie -ials u het bestand ter plaatse wilt bewerken:

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

Antwoord 16

sed -i '1 h
1 !H
$ {
   x
   s/^option.*/option=value/g
   t
   s/$/\
option=value/
   }' /etc/fdm_monitor.conf

Laad het hele bestand in de buffer, verander aan het einde alle gebeurtenissen en als er geen verandering optreedt, voeg dan toe aan het einde


Antwoord 17

De antwoorden met grep zijn fout. Je moet een -x optie toevoegen om de hele regel overeen te laten komen, anders komen regels zoals #text to addnog steeds overeen als je precies text to addwilt toevoegen.

Dus de juiste oplossing is zoiets als:

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

Antwoord 18

Sed gebruiken:het wordt ingevoegd aan het einde van de regel. Je kunt natuurlijk ook zoals gewoonlijk variabelen doorgeven.

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

Oneliner sed gebruiken

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

Echo gebruikenwerkt misschien niet onder root, maar werkt wel zo. Maar het zal je niet in staat stellen om dingen te automatiseren als je op zoek bent om het te doen, omdat het om een ​​wachtwoord kan vragen.

Ik had een probleem toen ik probeerde te bewerken vanuit de root voor een bepaalde gebruiker. Alleen al het toevoegen van de $usernamewas een oplossing voor mij.

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

Antwoord 19

Ik ging dieper in op de grep/sed-oplossing van kev door variabelen in te stellen om duplicatie te verminderen.

Stel de variabelen in op de eerste regel (hint: $_optionkomt overeen met alles op de regel tot de waarde [inclusief eventuele scheidingstekens zoals = of :]).

_file="/etc/ssmtp/ssmtp.conf" _option="mailhub=" _value="mijn.domein.tld" \
    sh -c '\
      grep -q "^$_option" "$_file" \
      &&; sed -i "s/^$_option.*/$_option$_value/" "$_file" \
      || echo "$_option$_value" >> "$_bestand"\
    '

Houd er rekening mee dat de sh -c '...'alleen het effect heeft dat de reikwijdte van de variabelen wordt vergroot zonder dat een exportnodig is. (Zie Een omgevingsvariabele instellen voor een commando in bash werkt niet voor tweede commando in een pipe)


Antwoord 20

U kunt deze functie gebruiken om configuratiewijzigingen te vinden en te doorzoeken:

#!/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

Antwoord 21

Als je deze opdracht wilt uitvoeren met een python-scriptbinnen een Linux-terminal…

import os,sys
LINE = 'include '+ <insert_line_STRING>
FILE = <insert_file_path_STRING>                                
os.system('grep -qxF $"'+LINE+'" '+FILE+' || echo $"'+LINE+'" >> '+FILE)

De $ en dubbele aanhalingstekens brachten me in een jungle, maar dit werkte.
Bedankt iedereen


Antwoord 22

Ik moest een bestand bewerken met beperkte schrijfrechten, dus ik had sudonodig. werken vanuit het antwoord van ghostdog74 en een tijdelijk bestand gebruiken:

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

Other episodes