J'utilise la commande sed
suivante pour remplacer certains paramètres dans un fichier de configuration:
sed -i 's/^option.*/option=value/g' /etc/fdm_monitor.conf
Maintenant j'ai un problème. Si la ligne n'existe pas, je veux l'ajouter au bas du fichier.
J'appelle cela avec un popen
sur un programme C. J'ai essayé d'utiliser awk
.
Essaye ça:
grep -q '^option' file && sed -i 's/^option.*/option=value/' file || echo 'option=value' >> file
En utilisant sed, la syntaxe simple:
sed \
-e '/^\(option=\).*/{s//\1value/;:a;n;ba;q}' \
-e '$aoption=value' filename
Cela remplacerait le paramètre s'il existe, sinon il serait ajouté au bas du fichier.
Utilisez l'option -i
si vous souhaitez modifier le fichier sur place.
Si vous voulez accepter et conserver les espaces, et en plus supprimer le commentaire, si la ligne existe déjà, mais est commentée, écrivez:
sed -i \
-e '/^#\?\(\s*option\s*=\s*\).*/{s//\1value/;:a;n;ba;q}' \
-e '$aoption=value' filename
Veuillez noter que ni l'option ni la valeur ne doivent contenir une barre oblique /
, sinon vous devrez l'échapper à \/
.
Pour utiliser les variables bash $option
et $value
, vous pouvez écrire:
sed -i \
-e '/^#\?\(\s*'${option//\//\\/}'\s*=\s*\).*/{s//\1'${value//\//\\/}'/;:a;n;ba;q}' \
-e '$a'${option//\//\\/}'='${value//\//\\/} filename
L'expression bash ${option//\//\\/}
cite des slash, elle remplace tout /
par \/
.
Note: i Je viens de me faire prendre au piège dans un problème. Dans bash, vous pouvez citer "${option//\//\\/}"
, mais dans le sh de busybox, cela ne fonctionne pas, vous devez donc éviter les guillemets, au moins dans les environnements autres que shell.
Tous combinés dans une fonction bash:
# 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
}
Explication:
/^\(option=\).*/
: Fait correspondre les lignes qui commencent par option=
et (.*
) ignorent tout ce qui se trouve après le =
. Le \(
… \)
inclut la partie que nous allons réutiliser comme \1
plus tard.#
au début de la ligne. \?
signifie «optionnel». Le commentaire sera supprimé car il se trouve en dehors de la partie copiée dans \(
… \)
. \s*
signifie «un nombre quelconque d'espaces blancs» (espace, tabulateur). Les espaces blancs sont copiés, car ils sont dans \(
… \)
, vous ne perdez donc pas le formatage./^\(option=\).*/{…}
: Si correspond à une ligne /…/
, alors exécutez la commande suivante. La commande à exécuter n'est pas une commande unique, mais un bloc {…}
.s//…/
: rechercher et remplacer. Puisque le terme recherché est vide //
, il s'applique à la dernière correspondance, qui était /^\(option=\).*/
.s//\1value/: Replace the last match with everything in
(…) , referenced by
\1and the text
value`:a;n;ba;q
: Définissez l'étiquette a
, puis lisez la ligne suivante n
, puis branchez b
(ou allez-y) à l'étiquette a
, ce qui signifie: lisez toutes les lignes jusqu'au et du fichier. Après la première correspondance, récupérez toutes les lignes suivantes sans autre En traitement. Alors q
guit et donc ignorer tout le reste.$aoption=value
: à la fin du fichier $
, ajoutez a
le texte option=value
Plus d'informations sur sed
et un aperçu des commandes sont sur mon blog:
Voici un one-liner sed qui fait le travail en ligne. Notez que il conserve l'emplacement de la variable et son indentation dans le fichier lorsqu'il existe. Ceci est souvent important pour le contexte, comme lorsqu'il y a des commentaires ou lorsque la variable est dans un bloc en retrait. Toute solution basée sur le paradigme "supprimer-ensuite-ajouter" échoue mal à cet égard.
sed -i '/^[ \t]*option=/{h;s/=.*/=value/};${x;/^$/{s//option=value/;H};x}' test.conf
Avec une paire générique variable/valeur, vous pouvez l'écrire de la manière suivante:
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
Enfin, si vous souhaitez également conserver des commentaires en ligne, vous pouvez le faire avec un groupe de captures. Par exemple. si test.conf
contient les éléments suivants:
a=123
# Here is "c":
c=999 # with its own comment and indent
b=234
d=567
Puis en cours d'exécution
var='c'
val='"yay"'
sed -i '/^[ \t]*'"$var"'=/{h;s/=[^#]*\(.*\)/='"$val"'\1/;s/'"$val"'#/'"$val"' #/};${x;/^$/{s//'"$var"'='"$val"'/;H};x}' test.conf
Produit que:
a=123
# Here is "c":
c="yay" # with its own comment and indent
b=234
d=567
En utilisant sed
, vous pourriez dire:
sed -e '/option=/{s/.*/option=value/;:a;n;:ba;q}' -e 'aoption=value' filename
Cela remplacerait le paramètre s'il existe, sinon il serait ajouté au bas du fichier.
Utilisez l'option -i
si vous souhaitez modifier le fichier sur place:
sed -i -e '/option=/{s/.*/option=value/;:a;n;:ba;q}' -e 'aoption=value' filename
En tant que ligne unique awk:
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] est votre entrée file
. Il est ouvert et écrit dans la boucle for
du bloc END
. L'ouverture de file
pour une sortie dans le bloc END
supprime le besoin d'utilitaires tels que sponge
ou l'écriture dans un fichier temporaire, puis mv
ing du fichier temporaire à file
.
Les deux assignations au tableau a[]
accumulent toutes les lignes de sortie dans a
. if(!f)a[++n]=s
ajoute le nouveau option=value
si la boucle awk principale n'a pas trouvé option
dans file
.
J'ai ajouté quelques espaces (pas beaucoup) pour la lisibilité, mais vous avez vraiment besoin d'un seul espace dans tout le programme awk, l'espace après print
. Si file
inclut # comments
, ils seront préservés.
voici un awk one-liner:
awk -v s="option=value" '/^option/{f=1;$0=s}7;END{if(!f)print s}' file
ceci ne fait pas de changement sur place sur le fichier, vous pouvez cependant:
awk '...' file > tmpfile && mv tmpfile file
Voici une implémentation awk
/^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"
}
Exécuter en utilisant
awk -f update.awk < /etc/fdm_monitor.conf > /etc/fdm_monitor.conf.tmp && \
mv /etc/fdm_monitor.conf.tmp /etc/fdm_monitor.conf
ou
awk -f update.awk < /etc/fdm_monitor.conf | sponge /etc/fdm_monitor.conf
EDIT: Comme un one-liner:
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
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
sed -i '1 h
1 !H
$ {
x
s/^option.*/option=value/g
t
s/$/\
option=value/
}' /etc/fdm_monitor.conf
Charger tout le fichier dans le tampon, à la fin, changer toutes les occurrences et si aucun changement ne se produit, ajouter à la fin
J'ai élaboré la solution grep/sed de kev en définissant des variables afin de réduire les duplications.
Définissez les variables dans la première ligne (indice: $_option
doit correspondre à tout le long de la ligne jusqu'à la valeur [y compris les séparateurs tels que = ou:]).
_ file = "/ etc/ssmtp/ssmtp.conf" _option = "mailhub =" _value = "mon.domaine.tld"\ sh -c '\ grep -q "^ $ _ option" "$ _file"\ && sed -i "s/^ $ _ option. */$ _ option $ _value /" "$ _file"\ || echo "$ _option $ _value" >> "$ _file"\ '
Rappelez-vous que le sh -c '...'
a simplement pour effet d'élargir la portée des variables sans qu'il soit nécessaire d'utiliser une export
. (Voir Définition d'une variable d'environnement avant qu'une commande dans bash ne fonctionne pas pour la deuxième commande dans un tube )
Vous pouvez utiliser cette fonction pour rechercher et rechercher des modifications de configuration:
#!/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