web-dev-qa-db-fra.com

sed: comment remplacer la ligne si trouvé ou ajouter à la fin du fichier si non trouvé?

Avec un seul fichier d'entrée contenant uniquement des commentaires (commençant par #) et des lignes de valeur VARIABLE =, est-il possible de remplacer une valeur pour une seule variable si elle est trouvée et, sinon, d'ajouter la paire à la fin du fichier si elle n'est pas trouvée?

Ma méthode actuelle fonctionne en la supprimant lors d'un premier passage, puis en l'ajoutant à la fin du fichier lors d'un second passage, mais cette méthode perturbe l'ordre des lignes (et comporte également deux commandes différentes):

sed -r "/^FOOBAR=.*$/d"      -i samefile &&
sed -r "$ a\FOOBAR=newvalue" -i samefile

Est-il possible de le faire, c.-à-d. garder l'ordre de la ligne, dans une seule ligne sed? Si un autre utilitaire (awk, ...) le fait, je le prendrais en charge.

33
BlakBat

C'est en fait assez simple avec sed: si une ligne correspond, copiez-la simplement dans l'espace hold, puis substitute la valeur.
Sur la la$t line exchangez les espaces de maintien et de motif, puis vérifiez si ce dernier est vide. Si ce n'est pas vide, cela signifie que la substitution a déjà été faite, donc rien à faire. Si elle est vide, cela signifie qu'aucune correspondance n'a été trouvée, alors remplacez l'espace de modèle par la variable désirée = value puis ajoutez-la à la ligne en cours dans le tampon de maintien. Enfin, exchange à nouveau:

sed '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile

La syntaxe ci-dessus est gnu sed. Portable:

sed '/^FOOBAR=/{
h
s/=.*/=newvalue/
}
${
x
/^$/{
s//FOOBAR=newvalue/
H
}
x
}' infile
28
don_crissti

Cela peut probablement être raccourci. Ce n'est pas une seule commande sed et il utilise également grep, mais cela semble être fondamentalement ce que vous voulez. Il s’agit d’une seule ligne et édite le fichier sur place (aucun fichier temporaire).

grep -q "^FOOBAR=" file && sed "s/^FOOBAR=.*/FOOBAR=newvalue/" -i file || 
    sed "$ a\FOOBAR=newvalue" -i file
18
snapshoe

Voici une approche sed plus simple, car je ne trouve pas sedhold space facile à utiliser. Si vous êtes à l'aise avec hold space, l'utilisation de l'approche don_crissti offre une possibilité supplémentaire de préserver un élément de la ligne existante, mais cela est généralement très rare.

Dans cette approche, il vous suffit d'imprimer toutes les lignes sauf la ligne que vous souhaitez supprimer, puis d'ajouter le remplacement à la fin.

sed -n -e '/^FOOBAR=/!p' -e '$aFOOBAR=newvalue' infile
11
haridsv

Sur la base des autres réponses, si vous voulez remplacer la valeur d'une variable si cette variable est présente dans le fichier et l'ajouter à la fin du fichier si ce n'est pas le cas (ce qui n'est pas ce que font vos commandes sed), vous pouvez essayer ceci:

Perl -ne '$c=1 if s/^FOOBAR=.*$/FOOBAR=newvalue/;  
             print; 
             END{print "FOBAR=newvalue" unless $c==1}' file > tmpfile && 
mv tmpfile file
3
terdon

C'est un peu plus simple dans awk, bien que l'édition "sur place" ne soit pas automatique:

awk -v varname="FOOBAR" -v newval="newvalue" '
    BEGIN {FS = OFS = "="}
    $1 == varname {$2 = newval; found = 1}
    {print}
    END {if (! found) {print varname, newval}}
' file > tempfile &&
mv tempfile file
1
glenn jackman

Utilisez simplement grep et echo pour créer un enregistrement vide:

grep -q '^FOOBAR=' somefile || echo 'FOOBAR=VALUE' >> somefile
sed -i 's/FOOBAR=.*$/FOOBAR=VALUE/' somefile 

Chaque ligne s'échappe avec le code d'erreur zéro.

0
MUY Belgium

Fonctionne réellement avec ce qui suit: sed -i '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile Dans la réponse choisie -i est manquant.

0
Yogesh Kamat