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.
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!
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.sed
comme suit: '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 :
sed
: http://man.cx/sedsed
(également utilisé sur macOS) reste proche de cette spécification, tandis que [~ # ~] gnu [ ~ # ~]sed
propose de nombreuses extensions.sed
et [~ # ~] bsd [~ # ~]sed
peut être trouvé à https://stackoverflow.com/a/24276470/45375La 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'
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.
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.
Malheureusement, pour moi, sed
semble ignorer \n
s 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
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.
Vous pouvez également utiliser le $'string'
fonction de Bash:
man bash | less -p "\\$'"
printf '%s' 'test1234foo123bar1234' | sed $'s/\\(1234\\)/\\\n\\1/g'
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