Comment détecter la fin de ligne avec sed

14

Je cherche un moyen d'exécuter le remplacement uniquement lorsque le dernier caractère est une nouvelle ligne, en utilisant sed.

Par exemple:

lettersAtEndOfLine

est remplacé, mais ce n'est pas:

lettersWithCharacterAfter&

Comme sedcela ne fonctionne pas bien avec les nouvelles lignes, ce n'est pas aussi simple que

$ sed -E "s/[a-zA-Z]*\n/replace/" file.txt

Comment cela peut-il être accompli?

Matthew D. Scholefield
la source

Réponses:

20

Avec standard sed, vous ne verrez jamais de nouvelle ligne dans le texte lu à partir d'un fichier. En effet, sedlit ligne par ligne, il n'y a donc pas de nouvelle ligne à la fin du texte de la ligne courante dans sedl'espace de motif de. En d'autres termes, sedlit les données délimitées par des sauts de ligne et les délimiteurs ne font pas partie de ce qu'un sedscript voit.

Les expressions régulières peuvent être ancrées à la fin de la ligne en utilisant $(ou au début, en utilisant ^). L'ancrage d'une expression au début / à la fin d'une ligne la force à correspondre exactement là, et pas seulement n'importe où sur la ligne.

Si vous souhaitez remplacer quelque chose correspondant au motif [A-Za-z]*à la fin de la ligne par quelque chose, puis ancrer le modèle comme ceci:

[A-Za-z]*$

... le forcera à correspondre à la fin de la ligne et nulle part ailleurs.

Cependant, puisque ne[A-Za-z]*$ correspond également à rien (par exemple, la chaîne vide présente à la fin de chaque ligne), vous devez forcer la correspondance de quelque chose , par exemple en spécifiant

[A-Za-z][A-Za-z]*$

Ainsi, votre ligne de commande sed sera donc

$ sed 's/[A-Za-z][A-Za-z]*$/replace/' file.txt

Je n'ai pas utilisé le -Ecommutateur ici car il n'est pas nécessaire. Avec ça, vous auriez pu écrire

$ sed -E 's/[A-Za-z]+$/replace/' file.txt

C'est une question de goût.

Kusalananda
la source
Bien que je sache comment le faire, vous obtiendrez un +1 juste pour utiliser le terme technique pour cela. :) C'est ce qu'on appelle l'ancrage - bon à savoir. Pour l'instant, j'ai toujours dû le paraphraser ... Une autre remarque sur le+ : vous pouvez l'utiliser même sans utiliser l'expression régulière étendue, n'oubliez pas de l'écrire comme \+. Cela sed -e 's/[A-Za-z]\+$/replace/' file.txtfonctionnera donc parfaitement même sans GNU sedinstallé. Et à ne pas oublier: ne l'utilisez pas -E, car GNU sedne le prend pas en charge .
erreur de syntaxe
1
@syntaxerror - Je pense que vous pouvez supprimer la dernière phrase ou au moins la mettre en gras comme gnu sedle soutient définitivement-E .
don_crissti
@don_crissti Eh bien, je pensais que vous étiez sur ce réseau depuis assez longtemps pour savoir qu'il n'y avait aucun moyen de déplier des parties d'un commentaire (à moins que vous ne le réécriviez entièrement). Alors permettez-moi de corriger: GNU sedpeut prendre en charge "silencieusement" -E, mais il n'est pas documenté dans la page de manuel (ni dans le manuel Texinfo (vérifié les deux)). Par conséquent, j'ai supposé qu'il n'était pas pris en charge (ce qui était une fausse hypothèse, après tout). Quoi qu'il en soit, vous avez raison, car au moins GNU sedne se plaindra pas si vous utilisez cette option.
erreur de syntaxe
@don_crissti Heureux que vous l'ayez fait! Donc, au moins, il a été confirmé qu'il sed faudra une option particulière qui n'a pas encore été correctement documentée. Cela est toujours utile; si personne n'est au courant du manque de documentation, personne ne le corrigera jamais.
erreur de syntaxe
@syntaxerror, voir unix.stackexchange.com/a/310454/135943 . Bien sûr, si vous devez travailler avec d'anciens systèmes tels que RHEL 5, vous utiliserez une version GNU sed qui ne prend pas en charge -E.
Wildcard
3
sed "s/[a-zA-Z]*$/replace/" input.txt > result.txt

Ou, la manière inutile longue et complexe:

J'ai découvert que cela peut être fait, en utilisant toujours sed, avec l'aide de tr. Vous pouvez attribuer un autre caractère pour représenter la fin de la ligne. Un autre caractère temporaire doit être utilisé, dans ce cas "" ". Utilisons "~" pour représenter la fin de la ligne:

tr '\n' '`' <input.txt >output.txt
sed -i "s/`/~`/" output.txt
tr '`' '\n' <output.txt >result.txt

Et puis pour effectuer la recherche réelle et remplacer, utilisez "~" plutôt que "\ n":

sed -i -E "s/[a-zA-Z]*~/replace/" result.txt

Et puis nettoyez le caractère supplémentaire sur les autres lignes:

sed -i "s/~//" result.txt

Évidemment, tout cela peut être assemblé, ce qui donne quelque chose comme:

tr '\n' '`' <input.txt | sed -e "s/`/~`/" | tr '`' '\n' | sed -E -e "s/[a-zA-Z]*~/replace/" | sed "s/~//" > result.txt
Matthew D. Scholefield
la source
3
Je ne suis pas sûr de comprendre ... Pourquoi ne pas simplement ancrer en fin de ligne avec $? par exemples/[a-zA-Z]*$/replace/
don_crissti
1
2 points: 1) Vous feriez mieux d'utiliser \+au lieu de *puisque ce dernier autorise zéro lettre à la fin de la chaîne; 2) Vous pouvez utiliser une classe de caractères [[:alpha:]]. Donc:sed 's/[[:alpha:]]\+$/replace/' file
glenn jackman
@glennjackman À quoi sert la barre oblique inverse avant le plus? Cela ne correspondrait-il pas au caractère d'addition?
Matthew D. Scholefield
1
GNU sed sans l' -roption utilise cette syntaxe d'expression régulière .
glenn jackman
0

À partir de l'extrait de code (cassé) que vous avez publié, vous semblez également vouloir remplacer la nouvelle ligne. Dans ce cas, l'ancrage regex par lui-même ne peut pas vous aider. Voici une solution:

sed '/[[:alpha:]]\+$/{N;s/[[:alpha:]]\+\n/replace/}' your_file

En panne:

  • /[a-zA-Z]\+$/{} signifie appliquer tout ce qui se trouve à l'intérieur des boucles aux lignes qui correspondent à l'expression régulière.
  • Le regex est celui qui utilise l'ancrage comme vu dans votre propre réponse , modifié pour tenir compte des commentaires de glenn jackman .
  • À l'intérieur des curlies, Nsignifie «ajouter la ligne suivante au tampon actif» (ce qui sedappelle «l'espace modèle»)
  • Enfin, la s///déclaration est votre substitution requise. Cela fonctionne maintenant car l'espace de motif contient deux lignes successives et la nouvelle ligne en fait donc partie.
Joseph R.
la source
0

Pour trouver la fin de la ligne, utilisez simplement le signe $ :

Sans ancrage de fin de ligne:

sed -n '/pattern/p' file 

Sans ancrage de fin de ligne:

sed -n '/pattern$/p' file
Utilisateur inconnu
la source