web-dev-qa-db-fra.com

Insérer un saut de ligne dans sed (Mac OS X)

Comment insérer une nouvelle ligne dans la pièce de rechange de sed?

Ce code ne fonctionne pas:

sed "s/\(1234\)/\n\1/g" input.txt > output.txt

où input.txt est:

test1234foo123bar1234

et output.txt doit être:

test
1234foo123bar
1234

mais au moment où j'obtiens ceci:

testn1234foo123barn1234

REMARQUE:

Cette question concerne spécifiquement la version Mac OS X de "sed", et la communauté a noté qu'elle se comportait différemment des versions Linux, par exemple.

39
Tyilo

Votre version sed ne prend apparemment pas en charge \n Dans RHS (côté droit de la substitution). Vous devriez lire LA FAQ SED maintenu par Eric Pement pour choisir une des solutions possibles. Je suggère d'essayer d'abord d'insérer un caractère de nouvelle ligne littéral.

Ci-dessous en est la citation.


4.1. Comment insérer une nouvelle ligne dans le RHS d'une substitution?

Plusieurs versions de sed permettent de saisir directement \n Dans le RHS, qui est ensuite converti en nouvelle ligne en sortie: ssed, gsed302a +, gsed103 (avec le commutateur -x), Sed15 +, sedmod, et UnixDOS sed. La solution la plus simple consiste à utiliser l'une de ces versions.

Pour les autres versions de sed, essayez l'une des méthodes suivantes:

(a) Si vous tapez le script sed à partir d'un Bourne Shell, utilisez une barre oblique inverse \ si le script utilise des "guillemets simples" ou deux barre oblique inverse \\ si le script nécessite des "guillemets doubles". Dans l'exemple ci-dessous, notez que le premier > Sur la 2e ligne est généré par le shell pour inviter l'utilisateur à entrer davantage de données. L'utilisateur tape dans une barre oblique, un guillemet simple, puis sur ENTRÉE pour terminer la commande:

 [sh-Prompt]$ echo twolines | sed 's/two/& new\
 >/'
 two new
 lines
 [bash-Prompt]$

(b) Utilisez un fichier script avec une barre oblique inverse \ dans le script, immédiatement suivi d'une nouvelle ligne. Cela va intégrer une nouvelle ligne dans la partie "remplacer". Exemple:

 sed -f newline.sed files

 # newline.sed
 s/twolines/two new\
 lines/g

Certaines versions de sed peuvent ne pas avoir besoin de la barre oblique inverse de fin. Si oui, supprimez-le.

(c) Insérez un caractère inutilisé et dirigez la sortie via tr:

 echo twolines | sed 's/two/& new=/' | tr "=" "\n"   # produces
 two new
 lines

(d) Utilisez la commande G:

G ajoute une nouvelle ligne, ainsi que le contenu de l'espace d'attente à la fin de l'espace de motif. Si l'espace d'attente est vide, une nouvelle ligne est ajoutée quand même. La nouvelle ligne est stockée dans l'espace modèle sous la forme \n Où elle peut être adressée en regroupant \(...\) et déplacée dans le RHS. Ainsi, pour changer l'exemple de "twolines" utilisé précédemment, le script suivant fonctionnera:

 sed '/twolines/{G;s/\(two\)\(lines\)\(\n\)/\1\3\2/;}'

(e) Insertion de lignes complètes, pas de rupture de lignes:

Si l'on ne change pas de lignes mais seulement en insérant des lignes complètes avant ou après un motif, la procédure est beaucoup plus facile. Utilisez la commande i (insérer) ou a (ajouter), en effectuant les modifications par un script externe. Pour insérer This line is new AVANT chaque ligne correspondant à une expression régulière:

 /RE/i This line is new               # HHsed, sedmod, gsed 3.02a
 /RE/{x;s/$/This line is new/;G;}     # other seds

Les deux exemples ci-dessus sont destinés à être des commandes "sur une ligne" entrées à partir de la console. Si vous utilisez un script sed, i\ Immédiatement suivi d'un saut de ligne littéral fonctionnera sur toutes les versions de sed. De plus, la commande s/$/This line is new/ Ne fonctionnera que si l'espace d'attente est déjà vide (ce qui est par défaut).

Pour ajouter This line is new APRÈS chaque ligne correspondant à une expression régulière:

 /RE/a This line is new               # HHsed, sedmod, gsed 3.02a
 /RE/{G;s/$/This line is new/;}       # other seds

Pour ajouter 2 lignes vides après chaque ligne correspondant à une expression régulière:

 /RE/{G;G;}                    # assumes the hold space is empty

Pour remplacer chaque ligne correspondant à une expression régulière par 5 lignes vides:

 /RE/{s/.*//;G;G;G;G;}         # assumes the hold space is empty

(f) Utilisez la commande y/// si possible:

Sur certaines versions Unix de sed (pas GNU sed!), Bien que la commande s/// N'accepte pas \n Dans le RHS, le y/// Si votre Unix sed le prend en charge, une nouvelle ligne après aaa peut être insérée de cette façon (qui n'est pas portable pour GNU sed ou autres seds):

 s/aaa/&~/; y/~/\n/;    # assuming no other '~' is on the line!
60
przemoc

Voici une solution monoligne qui fonctionne avec any compatible POSIX sed (y compris FreeBSD version sous macOS), en supposant que votre Shell est bash ou ksh ou zsh:

sed 's/\(1234\)/\'$'\n''\1/g' <<<'test1234foo123bar1234'

Notez que vous pourriez utiliser une chaîne unique ANSI C-quoted comme script entiersed, sed $'...' <<<, Mais cela nécessiterait \ - échapper à toutes les instances de \ (En les doublant), ce qui est assez lourd et entrave la lisibilité, comme en témoigne @ la réponse de tovk ).

  • $'\n' Représente une nouvelle ligne et est une instance de citation ANSI C, qui vous permet de créer des chaînes avec séquences d'échappement de caractères de contrôle.
  • Ce qui précède épissures la chaîne cotée C ANSI dans le script sed comme suit:
    • Le script est simplement divisé en 2 chaînes entre guillemets simples, avec la chaîne entre guillemets C ANSI coincée entre les deux moitiés :
    • 's/\(1234\)/\' est la 1ère moitié - notez qu'il se termine par \, afin d'échapper à la nouvelle ligne qui sera insérée comme caractère suivant. (cet échappement est nécessaire pour marquer la nouvelle ligne comme faisant partie de la chaîne de remplacement plutôt que d'être interprété comme la fin de la commande).
    • $'\n' Est la représentation entre guillemets C ANSI d'un caractère de nouvelle ligne, que le shell étend à une réelle nouvelle ligne avant de passer le script à sed.
    • '\1/g' Est la 2e mi-temps.

Notez que cette solution fonctionne de manière analogue pour les autres caractères de contrôle , tels que $'\t' Pour représenter un caractère de tabulation.


Informations générales :

  • La spécification POSIX sed: http://man.cx/sed
    • [~ # ~] bsd [~ # ~]sed (également utilisé sur macOS) reste proche de cette spécification, tandis que [~ # ~] gnu [ ~ # ~]sed propose de nombreuses extensions.
  • Un résumé des différences entre [~ # ~] gnu [~ # ~]sed et [~ # ~] bsd [~ # ~]sed peut être trouvé à https://stackoverflow.com/a/24276470/45375
20
mklement0

La version solaris de sed je pourrais convaincre de travailler de cette façon (dans bash):

echo test1234foo123bar1234 | sed 's/\(1234\)/\
\1/g'

(vous devez mettre le saut de ligne directement après la barre oblique inverse).

Dans csh j'ai dû mettre une barre oblique inverse supplémentaire:

echo test1234foo123bar1234 | sed 's/\(1234\)/\\
\1/g'

La version Gnu de sed fonctionnait simplement en utilisant \n:

echo test1234foo123bar1234 | sed 's/\(1234\)/\n\1/g'
8
bmk

Perl fournit une syntaxe regex "étendue" plus riche qui est utile ici:

Perl -p -e 's/(?=1234)/\n/g'

signifie "remplacer une nouvelle ligne pour la correspondance de largeur nulle suivant le modèle 1234". Cela évite d'avoir à capturer et à répéter une partie de l'expression avec des références arrières.

8
Phil

Obtenez un GNU sed .

$ brew install gnu-sed

Ensuite, votre commande fonctionnera comme prévu:

$ gsed "s/\(1234\)/\n\1/g" input.txt
test
1234foo123bar
1234

nb: vous pouvez aussi obtenir GNU sed grâce aux ports mac également.

2
oDDsKooL

Malheureusement, pour moi, sed semble ignorer \ns dans la chaîne de remplacement.

$ echo test1234foo123bar1234 | sed "s/\(1234\)/\n\1/g"
testn1234foo123barn1234

Si cela vous arrive également, une alternative consiste à utiliser:

$ echo test1234foo123bar1234 | sed "s/\(1234\)/\\`echo -e '\n\r'`\1/g"

Cela devrait fonctionner n'importe où et produira:

test
1234foo123bar
1234

Pour votre exemple avec un input.txt fichier en entrée et output.txt en sortie, utilisez:

$ sed "s/\(1234\)/\\`echo -e '\n\r'`\1/g" input.txt > output.txt
2
rid

Essaye ça:

$ echo test1234foo123bar1234 | sed "s/\(1234\)/\n\1/g"
test
1234foo123bar
1234

De doc Sed Gn

g
    Apply the replacement to all matches to the regexp, not just the first. 
1
Fredrik Pihl

Vous pouvez également utiliser le $'string' fonction de Bash:

man bash | less -p "\\$'"

printf  '%s' 'test1234foo123bar1234'  | sed $'s/\\(1234\\)/\\\n\\1/g'
1
tovk

La nouvelle ligne au milieu de la commande peut sembler un peu maladroite:

$ echo abc | sed 's/b/\
/'
a
c

Voici deux solutions à ce problème qui, à mon avis, devraient être assez portables (devraient fonctionner pour tout sh, printf et sed) compatible avec POSIX:

Solution 1:

N'oubliez pas d'échapper à tout \ et % caractères pour printf ici:

$ echo abc | sed "$(printf 's/b/\\\n/')"
a
c

Pour éviter d'avoir à s'échapper \ et % caractères pour printf:

$ echo abc | sed "$(printf '%s\n%s' 's/b/\' '/')"
a
c

Solution 2:

Créez une variable contenant une nouvelle ligne comme ceci:

newline="$(printf '\nx')"; newline="${newline%x}"

Ou comme ça:

newline='
'

Ensuite, utilisez-le comme ceci:

$ echo abc | sed "s/b/\\${newline}/"
a
c
1