Supposons que j'ai la chaîne 'abbc' et que je souhaite remplacer:
Si j'essaye deux remplace 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
EDIT: En fait, le texte pourrait avoir plus de 2 motifs et je ne sais pas combien de remplacements il me faudra. Comme il y avait une réponse disant que sed
est un éditeur de flux et que ses remplacements se font avec avidité, je pense que je devrai utiliser un langage de script pour cela.
Peut-être quelque chose comme ça:
sed 's/ab/~~/g; s/bc/ab/g; s/~~/bc/g'
Remplacez ~
par un caractère dont vous savez qu'il ne fera pas partie de la chaîne.
J'utilise toujours plusieurs déclarations 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
Ceci ajoutera un '\ n' avant tous les AND, GROUP BY, UNION et FROM, alors que '&' signifie la chaîne correspondante et '\ n &' signifie que vous souhaitez remplacer la chaîne correspondante par un '\ n' avant le 'correspondant '
Voici une variante de réponse de ooga qui fonctionne pour plusieurs paires de recherche et 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
désigne les limites du mot, 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 Word, cette technique peut ne pas fonctionner.
Notez également que cela donne les mêmes résultats que lorsque vous supprimez le s/________//g
et que vous ajoutez && sed -i 's/________//g' path_to_your_files/*.txt
à la fin de la commande, mais vous n'avez pas besoin de spécifier le chemin deux fois.
Une variante générale à ce sujet consisterait à utiliser \x0
ou _\x0_
à la place de ________
si vous savez qu'aucune valeur NULL n'apparaît dans vos fichiers, comme suggéré jthill .
sed
est un éditeur de flux. Il cherche et remplace goulûment. La seule façon de faire ce que vous avez demandé est d'utiliser un modèle de substitution intermédiaire et de le modifier à 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 correspondance préparée et conservée dans l'espace de blocage (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 poursuivre la recherche sur toute la longueur de la ligne. Une fois que le marqueur a atteint la fin de la ligne, le processus est terminé et la table de recherche est imprimée et les marqueurs sont supprimés.
N.B. La table de consultation est préparée au tout début et un deuxième marqueur unique (dans ce cas :
) est choisi de manière à 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
Tcl a un construit pour cela
$ tclsh
% string map {ab bc bc ab} abbc
bcab
Cela fonctionne en parcourant la chaîne d'un caractère à la fois en effectuant 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
Peut-être une approche plus simple pour une occurrence de modèle unique que vous pouvez essayer comme suit: echo 'abbc' | sed 's/ab/bc /; s/bc/ab/2'
Ma sortie:
~# echo 'abbc' | sed 's/ab/bc/;s/bc/ab/2'
bcab
Pour plusieurs occurrences de motif:
sed 's/\(ab\)\(bc\)/\2\1/g'
Exemple
~# cat try.txt
abbc abbc abbc
bcab abbc bcab
abbc abbc bcab
~# sed 's/\(ab\)\(bc\)/\2\1/g' try.txt
bcab bcab bcab
bcab bcab bcab
bcab bcab bcab
J'espère que cela t'aides !!
Voici un awk
basé sur oogas sed
echo 'abbc' | awk '{gsub(/ab/,"xy");gsub(/bc/,"ab");gsub(/xy/,"bc")}1'
bcab