Commentez toutes les lignes de la dernière ligne commentée à la ligne avec 'foo'
12
Considérez un fichier texte users.txt:
#alice
#bob
charlie
dotan
eric
Je dois tout commenter depuis (exclusif) la dernière ligne commentée jusqu'à (inclus) dotan. Voici le résultat:
#alice
#bob
#charlie
#dotan
eric
Y a-t-il un bon sedoneliner pour le faire? Je serai content de n'importe quel outil, pas seulement sed, vraiment.
Actuellement, je reçois le numéro de ligne de la dernière ligne commentée:
$ cat -n users.txt | grep '#' | tail -n1
2 #bob
J'en ajoute ensuite un et commente avec sed:
$ sed -i'' '3,/dotan/ s/^/#/' users.txt
Je sais que je pourrais être intelligent et mettre tout cela ensemble avec certains bcdans un monoligne laid. Il doit sûrement y avoir un moyen plus propre?
Si les lignes commentées existantes forment un seul bloc contigu, vous pouvez alors faire correspondre à partir de la première ligne commentée, en mettant en commentaire uniquement les lignes jusqu'à et y compris votre modèle de fin qui ne sont pas déjà commentées.
sed '/^#/,/dotan/ s/^[^#]/#&/' file
Si les commentaires existants ne sont pas contigus, alors en raison de la nature gourmande du match de la gamme sed, je pense que vous devrez faire quelque chose comme
tac file | sed '/dotan/,/^#/ s/^[^#]/#&/' | tac
c'est-à-dire correspondre vers le haut depuis le motif de fin jusqu'au «premier» commentaire - évidemment ce n'est pas si pratique si vous voulez une solution sur place.
Vous pouvez gérer les deux cas (lignes commentées dans un seul bloc contigu ou entrecoupées de lignes non commentées) avec une seule sedinvocation:
sed '1,/PATTERN/{/^#/{x;1d;b};//!{H;/PATTERN/!{1h;d};//{x;s/\n/&#/g}}}' infile
Cela ne traite que les lignes de la 1,/PATTERN/gamme. Il xchange l'espace de maintien w. l'espace de motif à chaque fois qu'une ligne est commentée (donc il n'y a jamais plus d'une ligne commentée dans le tampon de maintien) et ajoute chaque ligne qui n'est pas commentée à l' Hancien espace (quand sur la 1ère ligne, 1det respectivement 1hsont également nécessaires pour supprimer l'initiale ligne vide dans le tampon de maintien).
Lorsqu'il atteint la ligne correspondant au MOTIF, il l'ajoute également à l' Hancien tampon, e xmodifie les tampons, puis remplace chaque \ncaractère de ligne électronique dans l'espace de modèle par une ligne \nélectronique et un #(c'est-à-dire que toutes les lignes de l'espace de modèle commenceront maintenant par #, inclure la première ligne comme première ligne dans l'espace d'attente est toujours une ligne commentée).
Avec un échantillon infile:
alice
#bob
bill
#charlie
ding
dong
dotan
jimmy
#garry
fonctionnement:
sed '1,/dotan/{ # if line is in this range -start c1
/^#/{ # if line is commented -start c2
x # exchage hold space w. pattern space
1d # if 1st line, delete pattern space
b # branch to end of script
} # -end c2
//!{ # if line is not commented -start c3
H # append to hold space
/dotan/!{ # if line doesn't match dotan -start c4
1h # if 1st line, overwrite hold space
d # delete pattern space
} # -end c4
//{ # if line matches dotan -start c5
x # exchage hold space w. pattern space
s/\n/&#/g # add # after each newline character
} # -end c5
} # -end c3
}' infile # -end c1
les sorties:
alice
#bob
bill
#charlie
#ding
#dong
#dotan
jimmy
#garry
c'est donc commenter uniquement les lignes de (et exclure) #charliejusqu'à (et y compris) dotanet laisser les autres lignes intactes.
Bien sûr, cela suppose qu'il y a toujours au moins une ligne commentée avant la correspondance de ligne PATTERN. Si ce n'est pas le cas, vous pouvez ajouter une vérification supplémentaire avant le remplacement:/^#/{s/\n/&#/g}
Merci, j'aurai beaucoup à apprendre de cette réponse!
dotancohen
Attends, je dois avoir foiré. Il ne s'agit pas de la dernière série de lignes commentées? Non, je comprends, ça l'est. La dernière série + dotan. Assez sacrément intelligent.
mikeserv
1
Vous trouvez toujours les meilleures questions. Maudit dotan m'a fait jeter pendant un certain temps - peut-être encore, je ne l'ai pas encore testé. merci, don.
mikeserv
2
En voici une autre sed:
sed -e:n -e'/\n#.*\ndotan/!{$!{N;/^#/bn' \-eb -e\} -e'/^#/s/\(\n\)\(dotan.*\)*/\1#\2/g' \-et -e\} -eP\;D <in>out
Cela fait ce que vous demandez. Il fonctionne simplement sur une pile - en la construisant si nécessaire et aussi longtemps que nécessaire entre les occurrences de lignes commentées, et en vidant l'ancien tampon en faveur de la nouvelle ligne commentée plus loin en entrée lorsqu'il en trouve une. Image...
Désolé, je ne sais pas pourquoi j'ai fait ça. Mais cela m'est venu à l'esprit.
Quoi qu'il en soit, sedrépartit ses tampons entre chaque dernière ligne commentée dans n'importe quelle série, ne conservant jamais un seul plus dans son tampon que nécessaire pour suivre avec précision la dernière occurrence commentée, et si à tout moment il rencontre la dernière ligne en faisant cela, il tentera la La dernière ginstruction d'exécution tglobale et la branche est l'ensemble du tampon à imprimer, sinon elle imprimera Ptoutes les lignes qu'elle libère de son tampon dès qu'elle le fera.
Je suppose que c'est ce qui a fait penser aux accordéons ...
printf %s\\n \#alice \#bob charlie dotan eric \
\#alice \#bob charlie dotan eric \
\#alice \#bob charlie dotan eric |
sed -e:n -e'l;/\n#.*\ndotan/!{$!{N;/^#/bn' \-eb -e\} -e'/^#/s/\(\n\)\(dotan.*\)*/\1#\2/g' \-et -e\} -eP\;D
#alice
#alice\n#bob$
#alice\n#bob\ncharlie$
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob\ncharlie\ndotan\neric\n#alice$
#bob\ncharlie\ndotan\neric\n#alice\n#bob$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
#bob
charlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
charlie
dotan\neric\n#alice\n#bob\ncharlie\ndotan$
dotan
eric\n#alice\n#bob\ncharlie\ndotan$
eric
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob\ncharlie\ndotan\neric\n#alice$
#bob\ncharlie\ndotan\neric\n#alice\n#bob$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
#bob
charlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
charlie
dotan\neric\n#alice\n#bob\ncharlie\ndotan$
dotan
eric\n#alice\n#bob\ncharlie\ndotan$
eric
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob
#charlie
#dotan
eric
Il n'y a qu'une seule différence entre cette commande et celle ci-dessus et c'est la lcommande ook en haut. Lorsque nous lOOK à sedl'espace modèle de » que cela fonctionne , nous pouvons avoir une meilleure idée de ce qui se passe dans les coulisses et une meilleure compréhension de la façon de diriger ses efforts.
Dans ce cas, nous pouvons regarder l' sedentrée de la pile jusqu'à ce qu'elle ait trouvé une deuxième occurrence de \n#.*\ndotanin input, et cela lorsqu'il commence à imprimer la précédente une ligne à la fois. C'est plutôt cool. J'ai beaucoup appris en travaillant là-dessus.
Très bien merci! Le dernier paragraphe avec des explications est formidable, je vais aussi passer beaucoup de temps à apprendre de ce post. Belle pile!
dotancohen
1
@dotancohen - c'était une très bonne question. Jetez un œil à l'édition pour voir la pile .
mikeserv
2
Je remarque dans l'historique d'édition l'entrée Handle many dotans. Je suis sûr que c'est le pire cauchemar de ma femme.
dotancohen
1
@dotancohen - ouais, c'était difficile. Des trucs comme #\ndotan\ndotanc'est dur pour ces choses. Je veux dire quand je dis cela une bonne question. Je pense que je l'ai à peu près parfait, mais un problème que vous pourriez rencontrer est si vos blocs de commentaires sont séparés par 1000 lignes - cela le ralentira. Vous pouvez coller quelque chose comme s/\n/&/150;tavant la première /\n#chose pour casser le tampon s'il s'étend sur 150 lignes, par exemple. Et de toute façon, c'est peut-être ce qu'elle attend depuis le début !
En voici une autre
sed
:Cela fait ce que vous demandez. Il fonctionne simplement sur une pile - en la construisant si nécessaire et aussi longtemps que nécessaire entre les occurrences de lignes commentées, et en vidant l'ancien tampon en faveur de la nouvelle ligne commentée plus loin en entrée lorsqu'il en trouve une. Image...
Désolé, je ne sais pas pourquoi j'ai fait ça. Mais cela m'est venu à l'esprit.
Quoi qu'il en soit,
sed
répartit ses tampons entre chaque dernière ligne commentée dans n'importe quelle série, ne conservant jamais un seul plus dans son tampon que nécessaire pour suivre avec précision la dernière occurrence commentée, et si à tout moment il rencontre la dernière ligne en faisant cela, il tentera la La dernièreg
instruction d'exécutiont
globale et la branche est l'ensemble du tampon à imprimer, sinon elle imprimeraP
toutes les lignes qu'elle libère de son tampon dès qu'elle le fera.Je suppose que c'est ce qui a fait penser aux accordéons ...
Il n'y a qu'une seule différence entre cette commande et celle ci-dessus et c'est la
l
commande ook en haut. Lorsque nousl
OOK àsed
l'espace modèle de » que cela fonctionne , nous pouvons avoir une meilleure idée de ce qui se passe dans les coulisses et une meilleure compréhension de la façon de diriger ses efforts.Dans ce cas, nous pouvons regarder l'
sed
entrée de la pile jusqu'à ce qu'elle ait trouvé une deuxième occurrence de\n#.*\ndotan
in input, et cela lorsqu'il commence à imprimer la précédente une ligne à la fois. C'est plutôt cool. J'ai beaucoup appris en travaillant là-dessus.la source
Handle many dotans
. Je suis sûr que c'est le pire cauchemar de ma femme.#\ndotan\ndotan
c'est dur pour ces choses. Je veux dire quand je dis cela une bonne question. Je pense que je l'ai à peu près parfait, mais un problème que vous pourriez rencontrer est si vos blocs de commentaires sont séparés par 1000 lignes - cela le ralentira. Vous pouvez coller quelque chose commes/\n/&/150;t
avant la première/\n#
chose pour casser le tampon s'il s'étend sur 150 lignes, par exemple. Et de toute façon, c'est peut-être ce qu'elle attend depuis le début !