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 sed
te 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
-q
wees stil-x
komt overeen met de hele regel-F
patroon is een gewone string- https://linux.die.net/man/1/grep
Bewerken:
suggesties van @cerin en @thijs-woutersverwerkt.
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 grep
en echo
gebruikt 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 -s
toe 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 -i
als 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 $option
en $value
wilt 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 metoption=
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 text
waarde `:a;n;ba;q
: stel het labela
in, lees dan de volgende regeln
en vertakkingb
(of ga naar) terug naar labela
, 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 stoptq
en negeert daarom al het andere.$aoption=value
: voeg aan het einde van bestand$
a
de tekstoption=value
toe
Meer informatie over sed
en 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 --append
te 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.conf
het 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 file
voor uitvoer in het END
-blok vervangt de noodzaak voor hulpprogramma’s zoals sponge
of het schrijven naar een tijdelijk bestand en vervolgens mv
het tijdelijke bestand omzetten in file
.
De twee toewijzingen aan array a[]
verzamelen alle uitvoerregels in a
. if(!f)a[++n]=s
voegt de nieuwe option=value
toe als de hoofd awk-lus option
niet 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
# comments
bevat, 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 awk
implementatie
/^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 sed
zou 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 -i
als 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 add
nog steeds overeen als je precies text to add
wilt 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 $username
was 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: $_option
komt 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 export
nodig 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 sudo
nodig. 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