Je suis très nouveau dans sed, et tout ce que je trouve est un peu ici, un peu là.

J'ai un fichier texte qui contient un bloc comme le suivant:

#start
a
b
c


#whatever
…

Évidemment, c'est une version simplifiée. Je voudrais ajouter une ligne à la fin du bloc #start pour me donner:

#start
a
b
c
d

#whatever
…

Je peux en quelque sorte localiser le bloc avec les éléments suivants:

sed -n '/^#\s*start/,/^$/ p' data.txt

Donc je pense que c'est dans la bonne direction. Toutefois:

  • la sélection comprend la ligne vide, dont je ne veux pas
  • Je n'arrive pas à trouver comment ajouter une autre ligne après le match
2
Manngo 27 janv. 2019 à 08:11

5 réponses

Meilleure réponse

Avec sed:

sed '/#start/,/^$/ s/^$/d/;' file
  • /#start/,/^$/: rechercher des blocs commençant par #start et se terminant par une ligne vide
  • s/^$/d/: remplacez la ligne vide correspondante par un d

Si vous souhaitez ajouter la chaîne avant la ligne vide:

sed '/#start/,/^$/{/^$/{s//d/;G;};}' file
3
SLePort 27 janv. 2019 à 17:33

Pour GNU sed, essayez ceci s'il vous plaît (mise à jour à nouveau, a eu un petit bug):

sed '/^#\s*start/{x;s/.*/d/;x;be};/^\s*#[a-zA-Z]*/{x;G;x;s/.*//;x;};/^\s*$/{x;};:e'

Par exemple:

$ cat file                                                               
#start                                                                   
a                                                                        
b                                                                        
c                                                                        


#whatever                                                                
...                                                                      

$ cat file2                                                              
#start                                                                   
a                                                                        
b                                                                        
c                                                                        
#whatever                                                                
...                                                                                                       
$ sed '/^#\s*start/{x;s/.*/d/;x;be};/^\s*#[a-zA-Z]*/{x;G;x;s/.*//;x;};/^\s*$/{x;};:e' file  
#start                                                                   
a                                                                        
b                                                                        
c                                                                        
d                                                                        


#whatever                                                                
...                                                                      

$ sed '/^#\s*start/{x;s/.*/d/;x;be};/^\s*#[a-zA-Z]*/{x;G;x;s/.*//;x;};/^\s*$/{x;};:e' file2 
#start                                                                   
a                                                                        
b                                                                        
c                                                                        
d                                                                        
#whatever                                                                
...      

Cette commande sed va mélanger ces not-so-empty lignes vides (uniquement les lignes d'espacement). Mais je suppose que c'est bon pour toi, non?
L'idée est d'utiliser hold space pour garder l'élément à ajouter (d ici) et de l'ajouter le moment venu.
x doit échanger hold space avec pattern space (ligne de lecture actuelle).

Btw, s'il y a plusieurs blocs #start, il les ajoutera à tous, si vous ne voulez pas ce comportement, veuillez commenter.

0
Tiw 27 janv. 2019 à 05:47

Cela pourrait fonctionner pour vous (GNU sed):

sed '/^#start/,/^\s*$/!b;/^\s*$/c\d' file

Concentrez-vous sur la plage de lignes entre une ligne commençant par #start et une ligne vide. Si la ligne est vide, changez-la en d.

N.B. Le ! annule la correspondance et le b sans espace réservé, exclut tout traitement ultérieur par sed.

A titre de comparaison, notez le comportement de ces solutions qui dans le premier cas insère d avant la ligne vide et dans le second, ajoute d après la ligne vide.

sed '/^#start/,/^\s*$/!b;/^\s*$/i\d' file

sed '/^#start/,/^\s*$/!b;/^\s*$/a\d' file
0
potong 27 janv. 2019 à 12:58

Avec n'importe quel awk dans n'importe quel shell sur n'importe quelle boîte UNIX:

$ awk 'BEGIN{RS=""; ORS="\n\n"; FS=OFS="\n"} $1=="#start"{$(NF+1)="d"} 1' file
#start
a
b
c
d

#whatever
…

Contrairement à la solution sed actuellement acceptée, la méthode ci-dessus fonctionnera même si la chaîne cible contient des métacaractères d'expression régulière et même si la chaîne à ajouter contient des références arrière et même si l'une d'entre elles contient des délimiteurs (/) et qu'elle peut être modifiée de manière triviale en effectuez le changement en fonction de la valeur de la 2ème, 3ème ou de toute autre ligne du bloc d'entrée plutôt que ou en plus de la première ligne et il peut être modifié de manière triviale pour ajouter ou modifier n'importe quelle ligne au milieu du bloc. En bref, c'est une approche largement supérieure.

-1
Ed Morton 27 janv. 2019 à 15:35

Vous pouvez utiliser awk tout simplement comme ceci:

awk -v RS= '/#start/{$0 = $0 ORS "d\n"} 1' file

Si #start n'est pas en haut du fichier, vous voudrez faire:

awk -v RS= '/#start/{$0 = $0 ORS "d"} {$0 = $0 ORS} 1' file

* Cela supprime tous les sauts de ligne sauf un entre chaque bloc.

Résultat :

#start
a
b
c
d

#whatever
…
0
l'L'l 27 janv. 2019 à 06:48