Remplacer le contenu du groupe de capture regex à l'aide de sed

16

J'essaie de modifier mon fichier nginx.conf par programme, qui contient une ligne comme celle-ci:

        access_log /var/log/nginx/access.log;

que je veux ressembler à ceci:

        access_log /dev/stdout;

Je crois que l'expression régulière ^\s*access_log([^;]*);capturera la /var/log/nginx/access.logpièce dans un groupe de capture, mais je ne sais pas comment remplacer correctement le groupe de capture par sed?

J'ai essayé

echo "        access_log /var/log/nginx/access.log;" | sed 's/^\s*access_log([^;]*);/\1\\\/dev\\\/stdout/'

mais je reçois l'erreur:

sed: -e expression #1, char 45: invalid reference \1 on `s' command's RHS

si j'essaye sed -ralors il n'y a pas d'erreur, mais la sortie n'est pas ce que j'attends:

 /var/log/nginx/access.log\/dev\/stdout

J'essaie d'être intelligent avec le groupe de capture et ainsi de suite et de ne pas rechercher directement "access_log /var/log/nginx/access.log;" dans le cas où la distribution modifie l'emplacement du fichier journal par défaut.

Abe Voelker
la source
1
sed '/access_log/s|/[^;]\+|/dev/stdout|'
Costas
@Costas Si vous pouviez répondre à cette question, je vous voterais favorablement car cela fonctionne aussi!
Abe Voelker

Réponses:

16

Quelques erreurs là-bas.

Tout d'abord, puisque sedutilise des expressions régulières de base, vous avez besoin \(et \)pour créer un groupe de capture. Le -rcommutateur active les expressions régulières étendues, c'est pourquoi vous n'obtenez pas l'erreur. Voir Pourquoi dois-je échapper des caractères regex dans sed pour être interprétés comme des caractères regex? .

Deuxièmement, vous placez le groupe de capture au mauvais endroit. Si je vous comprends bien, vous pouvez le faire:

sed -e 's!^\(\s*access_log\)[^;]*;!\1 /dev/stdout;!' your_file

Notez l'utilisation de !délimiteurs en tant que regex pour éviter d'avoir à échapper les barres obliques vers l'intérieur /dev/stdout.

Joseph R.
la source
Merci, fonctionne à merveille! Je ne suis pas encore en mesure de marquer cela comme accepté, mais je reviendrai dans 9 minutes pour le faire.
Abe Voelker
1
@AbeVoelker Vous êtes les bienvenus. Je suis heureux d'avoir pu aider.
Joseph R.
5

Dans votre expression rationnelle, vous devez savoir que \No.c'est la référence arrière au motif entre crochets (). Donc regexp devrait être's@^(\s*access_log ).*$@\1/dev/stdout/;@'

Je peux offrir

sed '/access_log/s|/[^;]\+|/dev/stdout|'

CONSEIL: Que vous ayez l'intention d'utiliser /à l'intérieur du motif, vous êtes libre de changer s///pour chaque symbole que vous souhaitez s###par exemple

Costas
la source
Merci, j'aimerais pouvoir accepter deux réponses! Je ne regrette plus de ne pas avoir lu complètement les pages de manuel avant de demander de l'aide après avoir vu les solutions.
Abe Voelker
3

Plutôt que de le capturer, pourquoi ne pas simplement le trouver et faire une substitution? C'est à dire:

sed "/access_log/ s| /.*;| /dev/stdout;|"

Il recherche les lignes qui correspondent à " access_log" puis sur toutes les lignes qui le remplacent, il remplace " /whatever/path/is/there;" par " /dev/stdout;"

Jeremy Davis
la source