HervéRenault.fr

aide-mémoire à propos d'une ligne de commande

Recoller certaines lignes avec sed

J'avais un fichier TSV, dont certaines lignes étaient coupées par un retour chariot indésirable. Chaque ligne qui commençait par une tabulation devait donc être recollée à la ligne précédente. J'ai voulu le faire avec la commande sed, mais je me suis rendu compte que j'étais un peu rouillé pour faire plus qu'un simple s/foo/bar/. Je le note donc ici pour ne plus perdre de temps, un jour, à retrouver comment sed fonctionne.

J'avais donc des lignes de ce style :

Il	était	une	fois
trois	petits
	cochons
qui	vivaient	avec	leur	maman
dans	une
	petite	maison

Que je voulais remettre d'équerre comme ça :

Il	était	une	fois
trois	petits	cochons
qui	vivaient	avec	leur	maman
dans	une	petite	maison

1) Ma méthode inutilement compliquée 🙄

Il fallait donc que je demande à sed de regarder si la ligne courante (la ligne lue dans l'espace de travail, "pattern space" dans la doc en anglais) commence par une tabulation, et si c'est le cas, recoller avec la ligne précédente. Donc il fallait que je garde en permanence la ligne précédente dans le "hold space" (l'espace de rétention). Ce qui s'écrit en pseudo-algorithme :

Ce qui s'écrit en one-liner avec un peu d'espacement pour y voir clair :

sed -n '1{h;d}; /^\t/!{x;p}; /^\t/{x;G;s/\n//;p;n;h}; $p'

Je rappelle le contenu du fichier de départ, je numérote les lignes, et je décortique le fonctionnement ligne à ligne, un peu à la manière de sed --debug :

1 Il	était	une	fois
2 trois	petits
3 	cochons
4 qui	vivaient	avec	leur	maman
5 dans	une
6 	petite	maison
ligne lue
exécute
pattern space
hold space
1
1{h;d}
Il    était    une    fois
2
/^\t/!{x;p} Il    était    une    fois trois    petits
3
/^\t/{x;G;s/\n//;p;n;h}
trois    petits    cochons
juste avant la commande n
    cochons
juste avant la commande h
4 déjà lue par la commande n
qui    vivaient    avec    leur    maman
après la commande n, je n'affiche pas
qui    vivaient    avec    leur    maman
après la commande h
5
/^\t/!{x;p}
qui    vivaient    avec    leur    maman
cette fois j'affiche
dans    une
6
/^\t/{x;G;s/\n//;p;n;h}
dans    une    petite    maison
juste avant la commande n
    petite    maison
juste avant la commande h

2) La bonne méthode, celle du manuel 🤦

J'ai pourtant appris, dès que j'ai débuté, qu'il faut toujours RTFM… mais j'ai quand même passé quelques heures à mettre au point ma méthode avant de découvrir celle-ci qui est beaucoup plus simple, dans le manuel.

sed ':a ; $!N ; s/\n\t/\t/ ; ta ; P ; D'

Avec un peu de couleur pour faciliter la lecture :

sed ':a ; $!N ; s/\n\t/\t/ ; ta ; P ; D'

Bon, d'accord, sed c'est un peu barjot et j'aurais eu plus vite fait de le faire en Perl ou en PHP… Mais sed fait partie de la "culture Unix" donc c'est toujours un plaisir de l'utiliser 😉