Sed peut-il remplacer les caractères de nouvelle ligne?


43

Y at-il un problème avec sed et new line character?
J'ai un fichier test.txt avec le contenu suivant

aaaaa  
bbbbb  
ccccc  
ddddd  

Ce qui suit ne fonctionne pas:
sed -r -i 's/\n/,/g' test.txt

Je sais que je peux utiliser trpour cela, mais ma question est de savoir pourquoi cela ne semble pas possible avec sed.

S'il s'agit d'un effet secondaire du traitement du fichier ligne par ligne, je voudrais savoir pourquoi cela se produit. Je pense grepsupprime de nouvelles lignes. Est-ce que Sed fait la même chose?


1
Dans ce cas, sed n'est peut-être pas le meilleur outil à utiliser (par exemple, "tr"). Il existe des outils plus intuitifs, plus faciles à lire et à maintenir, plus performants (en particulier pour les données volumineuses), etc. ... N'utilisez pas votre marteau pour insérer les vis (même si cela fonctionne). Vous pouvez trouver une comparaison sur: http://slash4.de/blog/python/sed-replace-newline-or-python-awk-tr-perl-xargs.html
omoser le

2
trajouterait une fin ,et produirait une ligne non terminée. Le mieux est d'utiliser à la pasteplace:paste -sd , test.txt
Stéphane Chazelas

Réponses:


50

Avec GNU sedet fourniPOSIXLY_CORRECT n’est pas dans l’environnement (pour une entrée sur une seule ligne):

sed -i ':a;N;$!ba;s/\n/,/g' test.txt

De https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n :

  1. créer une étiquette via :a
  2. ajoute la ligne courante et la suivante à l’espace du motif via N
  3. si nous sommes avant la dernière ligne, branchez sur l’étiquette créée $!ba($! signifie ne pas le faire sur la dernière ligne (car il devrait y avoir une nouvelle ligne finale)).
  4. enfin, la substitution remplace chaque nouvelle ligne par une virgule dans l'espace de répétition (qui est le fichier entier).

Cela semble indiquer que le problème est que sed lit ligne par ligne.Mais je ne comprends pas pourquoi est-ce un problème.Il pourrait simplement lire la ligne et remplacer le caractère de la nouvelle ligne (ou le dernier caractère) par,
Jim

1
@jim On dirait que ce n'est pas dans la mémoire tampon pour faire correspondre, mais je ne parle pas couramment sed, quelqu'un d'autre peut peut-être nous éclairer à ce sujet. Je pense que vous devriez étendre votre Q avec cette information spécifique, afin que les gens soient plus enclins à la lire et, espérons-le, à y répondre.
Anthon

Cela se traduit parba: Event not found
krb686

@ krb686 Qu'est-ce que le "Ceci" auquel vous faites référence? Avez-vous exécuté la sedcommande ci-dessus avec ces options exactes? Sur quel test.txt dossier? Avec quelle version de sed(essayer sed --version)?
Anthon

@ Anthony Désolé, je pense que je voulais dire "le". J'ai lu un autre message SO qui m'a informé que csh exige que je m'échappe du !. Fait intéressant, cela ne fonctionnait toujours pas pour moi et j'ai fini par devoir échapper deux fois !à mon .cshscript. Donc, je n'ai pas vraiment de problème pour le moment, mais vous savez pourquoi cela pourrait être? Ce qui a fonctionné pour moi a étésed :a;N;$\\!ba;s/\n/ /g'
krb686

17

Cela fonctionne avec GNU sed:

sed -z 's/\n/,/g' 

-z est inclus depuis 4.2.2

NB -zchange le délimiteur en caractères nuls ( \0). Si votre entrée ne contient aucun caractère NULL, toute l'entrée est traitée comme une seule ligne. Cela peut venir avec ses limites .

Pour éviter de remplacer la nouvelle ligne de la dernière ligne, vous pouvez la rétablir:

sed -z 's/\n/,/g;s/,$/\n/'

(Ce qui est sedencore une fois la syntaxe GNU , mais cela n'a pas d'importance car tout est uniquement GNU)


3
Cela remplacera également la fin de ligne, ce qui pourrait ne pas être ce que veut OP ... comparez le résultat avec la solution de mikeserv .
don_crissti

7

Sur le site Web d'Oracle:

L'utilitaire sed fonctionne en lisant séquentiellement un fichier, ligne par ligne, dans la mémoire. Il exécute ensuite toutes les actions spécifiées pour la ligne et la remet en mémoire pour être transféré vers le terminal avec les modifications demandées. Une fois que toutes les actions ont été effectuées sur cette seule ligne, la ligne suivante du fichier est lue et le processus est répété jusqu'à ce que le fichier soit terminé.

En gros, cela signifie que, parce que sed lit ligne par ligne, le caractère de nouvelle ligne ne correspond pas.

La solution de https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n est la suivante:

sed ':a;N;$!ba;s/\n/,/g'

ou, dans une version portable (sans ;enchaîner après les étiquettes de saut)

sed -e ':a' -e 'N;$!ba' -e 's/\n/,/g'

Une explication de la façon dont cela fonctionne est fournie sur cette page.


J'ai utilisé une forme modifiée de cela pour analyser les journaux VPN et mettre les informations "authentifiées" de l'utilisateur sur l'horodatage sur la même ligne. À votre santé!
user208145

Notez que cette syntaxe est spécifique à GNU, et même avec GNU sed, si POSIXLY_CORRECT est dans l'environnement et que l'entrée n'a qu'une seule ligne, il n'y aura pas de sortie.
Stéphane Chazelas

5

sedsupprime toujours la fin de ligne \newline juste avant de remplir l’espace de modèle, puis en ajoute un avant d’écrire les résultats de son script. Une \newline peut être créée dans l'espace des motifs de différentes manières - mais jamais si elle ne résulte pas d'une modification. Ceci est important - les \nespaces dans sedl'espace de modèle de s reflètent toujours un changement et ne se produisent jamais dans le flux d'entrée. \nles lignes sont le seul délimiteur ased peut compter avec une entrée inconnue.

Si vous souhaitez remplacer toutes les \nlignes par des virgules et que votre fichier n'est pas très volumineux, vous pouvez procéder comme suit:

sed 'H;1h;$!d;x;y/\n/,/'

Cela ajoute chaque ligne d’entrée à l’ hancien espace - à l’exception de la première, qui remplace l’ hancien espace - à la suite d’un \ncaractère ewline. Il dsupprime ensuite chaque ligne, pas la $!dernière de la sortie. Sur la dernière ligne H, les espaces et les espaces de modèle sont xmodifiés et tous les \ncaractères ewline sont convertis y///en virgules.

Pour les fichiers volumineux, ce type de problème est inévitablement source de problèmes - sedla mémoire tampon de la ligne de démarcation peut être facilement surchargée d'actions de ce type.


2

Alternativement, vous pouvez utiliser une syntaxe légèrement plus simple:

sed ':a;N;s/\n/,/g;ba'

... juste changer l'ordre de la séquence.


3
Mais exécute la scommande pour chaque ligne d'entrée sur un espace de modèle de plus en plus grand.
Stéphane Chazelas

1

Il y a une très belle magie sed ici. Et quelques bons points soulevés au sujet du débordement d’espace-modèle J'adore utiliser sed, même si ce n'est pas la méthode la plus simple, car elle est si compacte et puissante. Cependant, il a ses limites et pour de grandes quantités de données, l'espace de modèle devrait être mahoosif.

GNU dit ceci:

Pour ceux qui souhaitent écrire des scripts sed portables, sachez que certaines implémentations sont connues pour limiter la longueur des lignes (pour le motif et les espaces de maintien) à un maximum de 4000 octets. La norme posix spécifie que les implémentations sed conformes doivent prendre en charge une longueur de ligne d'au moins 8192 octets. GNU sed n’a pas de limite intégrée sur la longueur de la ligne; tant que vous pouvez utiliser malloc () plus de mémoire (virtuelle), vous pouvez alimenter ou construire des lignes aussi longtemps que vous le souhaitez.
Cependant, la récursivité est utilisée pour gérer les sous-modèles et la répétition indéfinie. Cela signifie que l'espace de pile disponible peut limiter la taille de la mémoire tampon pouvant être traitée par certains modèles.

Je n'ai pas grand chose à ajouter, mais je voudrais vous indiquer mon guide de référence pour sed . C'est excellent. http://www.grymoire.com/Unix/Sed.html

et voici ma solution:

for i in $(cat test.txt); do echo -n $i','; done; echo '' >> somewhere

bien ça marche



-1

Disons que vous voulez remplacer les nouvelles lignes par \n. Je voulais faire ça, alors voici ce que j'ai fait:

(echo foo; echo bar; echo baz) | sed -r '$!s/$/\\n/' | tr -d '\n' 
# Output: foo\nbar\nbaz

Voici ce que cela fait: pour toutes les lignes sauf la dernière , ajouter \n. Ensuite, supprimez les nouvelles lignes avec tr.


-r est uniquement disponible dans GNU sed , pas BSD.
Kenorb
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.