web-dev-qa-db-fra.com

Utilisation de sed pour modifier l’apparition d’un motif correspondant

ne ligne commençant avec Fred Flintstone doit être ajouté avec une chaîne. Recherchez l'occurrence spécifiée de Fred Flintstone et ajoutez-la.

Comment utiliser cette commande pour n'importe laquelle des occurrences d'un tel modèle? j'ai essayé

sed '/Fred Flintstone/ s/$/ someString/2' filename

Apparemment, le précédent ne fonctionne pas. Cela fonctionne bien pour toutes les occurrences, mais pas pour une occurrence spécifique. (Disons que je veux remplacer le premier ou le deuxième ou le troisième [n'importe lequel])

Exemple de fichier1:

Fred Flintstone 
Johnson Stone
Fred Flintstone
Fred Flintstone
Michael Clark

Fichier de sortie souhaité 1:

Fred Flintstone 
Johnson Stone
Fred Flintstone someString
Fred Flintstone
Michael Clark
6
DazzlerJay

Bien que vous ayez mentionné sedname__, il s’agit en quelque sorte de awkname __- y tâches:

awk -v pat="Fred Flintstone" '$0 ~ pat {count++;\
               if (count == 2) { $0 = $0" someString" ;} ;}; 1' file.txt
  • -v pat="Fred Flintstone" enregistre le motif Regex correspondant en tant que variable patà utiliser dans les expressions awkname__

  • $0 ~ pat vérifie l'enregistrement avec patpour une correspondance; si elle correspond, la variable countest augmentée de 1 et si countvaut 2, l'enregistrement est réinitialisé avec le contenu actuel plus someString({count++; if (count == 2) { $0 = $0" someString" ;} ;})

  • 1 est un idiome; comme il est vérité , tous les enregistrements seront imprimés

Exemple:

% cat file.txt
Fred Flintstone
Johnson Stone
Fred Flintstone
Fred Flintstone
Michael Clark

% awk -v pat="Fred Flintstone" '$0 ~ pat {count++; if (count == 2) { $0 = $0" someString" ;} ;}; 1' file.txt
Fred Flintstone
Johnson Stone
Fred Flintstone someString
Fred Flintstone
Michael Clark
7
heemayl

Cette simple commande sed vous permet d'effectuer la modification de manière sélective sans utiliser de boucles (elle utilise une branche à la fin) ou nécessitant des extensions GNU ou lire le fichier entier à la fois:

sed -r '/Fred Flintstone/ {x; s/$/#/; /^#{2}$/ {x; s/.*/& someString/; b}; x}'

Explication:

  • -r - utilise l'expression régulière étendue
  • /Fred Flintstone/ - pour les lignes correspondant à ce modèle:
    • x - échange un espace modèle et le maintient (active le compteur)
    • s/$/#/ - ajoute un caractère au compteur
    • /^#{2}$/ - lorsque le compteur a la longueur 2 (remplacez une valeur)
      • x échange le motif et le garde (active la ligne d'entrée comptée)
      • s/.*/& someString/ - ajoute la chaîne à la ligne souhaitée
      • b - passe à la fin du traitement de cette ligne afin qu'elle puisse être imprimée
    • x - échange l'espace de modèle et le maintient (active les lignes qui correspondent à la chaîne mais pas au compte)

Les niveaux d'indentation dans l'explication indiquent les niveaux d'imbrication d'accolades.

Toutes les autres lignes passent sans traitement et sont imprimées.

5

Réponse révisée pour empêcher l’injection de code sur les barres obliques inverses ainsi que les barres obliques, guillemets lorsqu’il utilise awk les variables awk -v variable ... ou un shell awk '...' variable="$value" depuis awk effectue le traitement de la séquence d'échappement C sur les valeurs transmises avec -v variable= ou Shell variable=$value; donc le Shell variable='\\n' deviendra \n dans awk ).

pattern="${patt//\\/\\\\}" awk '
    $0 ~ ENVIRON["pattern"]{ seen++ } seen==2 { $0 = $0 "something"};1' infile

pour l'entrée ci-dessous et le motif :

another break line
Johnson Stone
\"Fred //\\Flintstone' SOMETHING will goes here ...
\"Fred //\\Flintstone'
Michael Clark

le motif est dans une variable appelée pattname__

$ echo "$patt"
\"Fred //\\Flintstone'

La sortie est:

\"Fred //\\Flintstone'
another break line
Johnson Stone
\"Fred //\\Flintstone' SOMETHING will goes here ...something
\"Fred //\\Flintstone'
Michael Clark

Explication:

plus tard, vous devrez peut-être passer la chaîne somethingen tant que variable, et vous devrez utiliser quelque chose comme edit=$somesting et y utiliser ENVIRON["edit"].

pattern="${patt//\\/\\\\}" edit="$something" awk '
    $0 ~ ENVIRON["pattern"]{ seen++ } seen==2 { $0 = $0 ENVIRON["edit"]};1' infile
5
αғsнιη

Voici un moyen de le faire dans sed sans mettre en mémoire le fichier entier:

  • trouver l'occurrence première du motif
  • ajoute la ligne suivante N à l'espace du motif
  • tentative de remplacement de la seconde occurrence du motif
  • revenir en arrière et ajouter une autre ligne si la correspondance échoue Ta

$q quitte la boucle si la fin du fichier est atteinte sans correspondance.

Alors

sed '/Fred Flintstone/ {:a; $q; N; s//& someString/2; Ta;}' File1
Fred Flintstone 
Johnson Stone
Fred Flintstone someString
Fred Flintstone
Michael Clark

Bien que T soit une extension GNU, vous pouvez faire de même dans POSIX sed en utilisant t; ba


Hmm ... après y avoir réfléchi un peu plus, le nouveau texte sera ajouté à toutes les autres occurrences - pas seulement à la seconde. Si vous devez vraiment utiliser ne remplacer que la deuxième occurrence, la seule façon pour moi de le faire serait:

  • adresse et remplace la première instance par une chaîne unique
  • adresse et modification de la nouvelle première instance
  • adresse et remplace la chaîne unique par le motif d'origine

GNU sed fournit une astuce pour adresser la première instance d'un motif

0,/pattern/

Alors

sed -e '0,/Fred Flintstone/ s//Barney Rubble/' \
    -e '0,/Fred Flintstone/ s//& someString/' \
    -e '0,/Barney Rubble/ s//Fred Flintstone/
' File1
Fred Flintstone
Johnson Stone
Fred Flintstone someString
Fred Flintstone
Michael Clark

Si cela ne vous dérange pas d'utiliser ed, vous pouvez vous adresser plus directement à la seconde instance en allant sur la première ligne, puis en faisant correspondre le transfert d'une instance à la suivante:

ed -s File1 << \EOF                                                                                                              
1;#
/Fred Flintstone/,/Fred Flintstone/ s//& someString/
,p
q
EOF
Fred Flintstone
Johnson Stone
Fred Flintstone someString
Fred Flintstone
Michael Clark

ou comme une ligne

printf '1;#\n/Fred Flintstone/,/Fred Flintstone/ s//& someString/\n,p\n' | ed -s File1

Vous pouvez rendre la version ed sur place en remplaçant ,p par w

4
steeldriver

Autre cas d'utilisation de ex, l'éditeur de fichier spécifié par POSIX

(C'est le prédécesseur de vi et fait toujours partie de vi.)

printf '%s\n' '0/Fred Flintstone///s/$/ someString/' x | ex filename

La magie est que contrairement à Sed, Awk et à des outils similaires, ex ne fait pas exécuter de code sur toutes les lignes. Vous pouvez déplacer le curseur, donner des commandes, etc.

Dans ce cas, nous donnons un numéro de ligne 0 (qui garantit que Fred Flintstone sera pris en compte sur la première ligne), suivi d'un regex /Fred Flintstone/ qui fait référence à la première ligne du fichier correspondant à cette regex, suivie d'un autre regex // qui, étant vide, réutilise la dernière expression rationnelle et renvoie donc à la deuxième seconde ligne du fichier qui correspond; Nous utilisons ensuite la commande s que vous connaissez déjà.

La commande x signifie enregistrer toutes les modifications et quitter.

Nous utilisons printf pour alimenter les commandes en ex.

2
Wildcard