Comment remplacer plusieurs modèles à la fois avec sed?
Supposons que j'ai une chaîne 'abbc' et que je veux remplacer:
- ab - > bc
- bc - > ab
Si j'essaie deux remplacements, le résultat n'est pas ce que je veux:
echo 'abbc' | sed 's/ab/bc/g;s/bc/ab/g'
abab
Alors, quelle commande sed puis-je utiliser pour remplacer comme ci-dessous?
echo abbc | sed SED_COMMAND
bcab
Modifier :
En fait, le texte pourrait avoir plus de 2 modèles et je ne sais pas combien de remplacements j'aurai besoin. Comme il y avait une réponse disant que sed
est un éditeur de flux et que ses remplacements sont avidement, je pense que j'aurai besoin de pour utiliser un langage de script pour cela.
7 réponses
Peut-être quelque chose comme ceci:
sed 's/ab/~~/g; s/bc/ab/g; s/~~/bc/g'
Remplacez ~
par un caractère que vous savez ne sera pas dans la chaîne.
Voici une variation sur la réponse de ooga {[12] } qui fonctionne pour plusieurs paires de recherche et de remplacement sans avoir à vérifier comment les valeurs peuvent être réutilisées:
sed -i '
s/\bAB\b/________BC________/g
s/\bBC\b/________CD________/g
s/________//g
' path_to_your_files/*.txt
Voici un exemple:
Avant:
some text AB some more text "BC" and more text.
Après:
some text BC some more text "CD" and more text.
Notez que \b
indique les limites des mots, ce qui empêche le ________
d'interférer avec la recherche (j'utilise GNU sed 4.2.2 sur Ubuntu). Si vous n'utilisez pas de recherche de limite de mot, cette technique peut ne pas travail.
Notez également que cela donne les mêmes résultats que la suppression du s/________//g
et l'ajout de && sed -i 's/________//g' path_to_your_files/*.txt
à la fin de la commande, mais ne nécessite pas de spécifier le chemin deux fois.
Une variante générale à ce sujet serait d'utiliser \x0
ou _\x0_
à la place de ________
Si vous savez qu'aucune valeur nulle n'apparaît dans vos fichiers, comme jthill l'a suggéré .
sed
est un éditeur de flux. Il recherche et remplace avidement. La seule façon de faire ce que vous avez demandé est d'utiliser un modèle de substitution intermédiaire et de le changer à la fin.
echo 'abcd' | sed -e 's/ab/xy/;s/cd/ab/;s/xy/cd/'
Cela pourrait fonctionner pour vous (GNU sed):
sed -r '1{x;s/^/:abbc:bcab/;x};G;s/^/\n/;:a;/\n\n/{P;d};s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/;ta;s/\n(.)/\1\n/;ta' file
Cela utilise une table de recherche qui est préparée et maintenue dans L'espace de retenue (HS), puis ajoutée à chaque ligne. Un marqueur unique (dans ce cas \n
) est ajouté au début de la ligne et utilisé comme méthode pour bump-le long de la recherche sur toute la longueur de la ligne. Une fois que le marqueur atteint la fin de la ligne, le processus est terminé et est imprimé sur la table de recherche et les marqueurs étant rejetés.
N.B. la table de recherche est préparé au tout début et un deuxième marqueur unique (dans ce cas :
) choisi afin de ne pas entrer en conflit avec les chaînes de substitution.
, Avec quelques commentaires:
sed -r '
# initialize hold with :abbc:bcab
1 {
x
s/^/:abbc:bcab/
x
}
G # append hold to patt (after a \n)
s/^/\n/ # prepend a \n
:a
/\n\n/ {
P # print patt up to first \n
d # delete patt & start next cycle
}
s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/
ta # goto a if sub occurred
s/\n(.)/\1\n/ # move one char past the first \n
ta # goto a if sub occurred
'
La table fonctionne comme ceci:
** ** replacement
:abbc:bcab
** ** pattern
J'utilise toujours plusieurs instructions avec "- e "
$ sed -e 's:AND:\n&:g' -e 's:GROUP BY:\n&:g' -e 's:UNION:\n&:g' -e 's:FROM:\n&:g' file > readable.sql
Cela ajoutera un '\n 'Avant all AND's, GROUP BY's, UNION's et FROM, alors que' & ' signifie la chaîne correspondante et '\n& 'signifie que vous voulez remplacer la chaîne correspondante par un '\n 'avant le' matched '
Tcl a un intégré pour ce
$ tclsh
% string map {ab bc bc ab} abbc
bcab
Cela fonctionne en parcourant la chaîne d'un caractère à la fois en faisant des comparaisons de chaînes à partir de la position actuelle.
En perl:
perl -E '
sub string_map {
my ($str, %map) = @_;
my $i = 0;
while ($i < length $str) {
KEYS:
for my $key (keys %map) {
if (substr($str, $i, length $key) eq $key) {
substr($str, $i, length $key) = $map{$key};
$i += length($map{$key}) - 1;
last KEYS;
}
}
$i++;
}
return $str;
}
say string_map("abbc", "ab"=>"bc", "bc"=>"ab");
'
bcab
Voici un awk
basé sur oogas sed
echo 'abbc' | awk '{gsub(/ab/,"xy");gsub(/bc/,"ab");gsub(/xy/,"bc")}1'
bcab