2
votes

bash / sed, remplace la ligne dans le fichier au même niveau d'indentation

Je cherche à remplacer des lignes dans un fichier. c'est-à-dire que si "oldtext" est dans le fichier au début d'une ligne, remplacez la ligne entière par "newtext".

Le début de la ligne peut être ^oldtext ou oldtext sera indenté par des espaces / tabulations

sed -i -e 's/^$prefixspaces$newtext*$/$prefixspaces$newtext/g' myfile.txt

J'ai besoin de calculer le même niveau d'indentation que "oldtext"

prefixspaces = <number of spaces before the "mytext", converting tabs to 4 spaces>

Je me rends compte que sed est peut-être le meilleur moyen d'y parvenir, mais je dois d'abord calculer l'espace de préfixe, puis ma sortie est quelque chose comme:

grep "^[\S]oldtext" myfile.txt   # doesn't work

Je ne sais pas comment calculer cela. Peut-être que $prefixspace peut être $prefixspace dans un groupe () ou quelque chose pour que sed puisse facilement le calculer et travailler avec? C'est peut-être un one-liner sed ou awk, mais je ne peux pas le résoudre après quelques recherches.


5 commentaires

Pourriez-vous s'il vous plaît publier un échantillon de l'entrée et de la sortie attendue dans votre question pour le rendre plus clair.


s/^\([[:space:]]*\)oldtext/\1$newtext/ ?


C'est génial @Shawn, j'ai ajouté .* Après oldtext pour qu'il remplace toute la ligne et qu'il veuille n'importe quel espace, alors il l'a changé en \s* au début de la ligne. Fonctionne bien.


Vous devriez tester les réponses que vous avez obtenues lorsque oldtext contient des métacaractères de regexp et newtext contient des références arrière. Par exemple, essayez-les quand oldtext vaut ac et que le fichier d'entrée contient à la fois ac et abc , et quand newtext vaut d&e . Vous devriez faire des correspondances de chaînes littérales et des remplacements de chaînes littérales, pas des correspondances d'expression régulière et des remplacements de chaînes activés par les références rétrospectives, qui sont tout ce que sed prend en charge et jusqu'à présent toutes les réponses le font.


ok, avez-vous un moyen de résoudre ce problème Ed?


3 Réponses :


3
votes

Comme ça:

\1            the 0 or more spaces from group1
newtext       'newtext'

Explication:

chercher

^             beginning of the line
\( *\)        capture 0 or more spaces in group 1
oldtext       'oldtext'
.*            rest of the line

remplacer

sed 's/^\( *\)oldtext.*/\1newtext/' file


0 commentaires

1
votes

Avec sed

Une autre façon de faire, en plus de la réponse acceptée, consiste à utiliser des caractères de classe ([[: space:]]) et des expressions régulières d'extension avec le paramètre -r :

  newtext
newtext
    newtext
  newtext

Et le résultat sera:

$ perl -lpe 's/^(\s*)oldtext.*/\1newtext/' << _eof
  oldtext this
oldtext this
    oldtext this
  oldtext also this
_eof

Dans ([[:space:]]*) , nous avons capturé un groupe avec zéro ou plusieurs espaces, mais nous pouvons spécifier un certain nombre.

Avec awk

Nous pouvons utiliser gsub pour remplacer les espaces et compter les temps:

  newtext
 newtext
     newtext
   newtext

Et le résultat sera:

$ awk \
    -v old="oldtext" \
    -v new="newtext" \
    'BEGIN{re="^[[:space:]]*"old}
{
  if (match($0,re)) {
    i=gsub(/ /,"")
    while (--i) printf " "
    print new
  }
  else 
    print
}' << _eof
  oldtext this
oldtext also this
    oldtext and this
  oldtext also this
_eof

Avec perl

Nous pouvons y parvenir avec l'utilisation de perl dans un style de type sed :

  newtext
 a
    newtext

Et le résultat sera:

$ sed -r 's/^([[:space:]]*)oldtext.*/\1newtext/' << _eof
  oldtext this
 a
    oldtext also this
_eof

0 commentaires

1
votes

Cela pourrait fonctionner pour vous (GNU sed):

sed '/^\s*oldtext/s/\S.*/newtext/' file

Correspond à l' oldtext au début d'une ligne ou en retrait et remplacez-le à partir du premier caractère d'espacement non blanc.


0 commentaires