Cette question m'a aidé à comprendre comment combiner stderr et stdout:
Comment rediriger stderr vers un fichier
avec cette commande:
gunzip -vc /opt/minecraft/wonders/logs/20* 2>&1
Mais comment peut-on insérer un saut de ligne entre les deux pour que la sortie apparaisse sur des lignes séparées au lieu d'être groupées?
Le stderr vient en premier, qui est toujours une ligne, quelque chose comme ceci:
/opt/minecraft/wonders/logs/2017-08-28-2.log.gz:
Alors que stdout est généralement (mais pas toujours) plusieurs lignes, comme:
06:17:05: Starting minecraft server version 1.10.2
06:18:21: Loading properties
06:18:21: Default game type: SURVIVAL
06:18:21: Generating keypair
06:18:21: Starting Minecraft server on *:25565
06:18:22: Using default channel type
Mais comme il n'y a pas de saut de ligne entre eux, la première ligne de chaque fichier à décompresser ressemble toujours à ceci:
/opt/minecraft/wonders/logs/2018-08-18-2.log.gz: 06:17:05: Starting minecraft server version 1.10.2
J'ai besoin de ces deux parties sur des lignes différentes pour pouvoir les filtrer avec grep. Mais je veux aussi que le stdout correspondant suive directement son stderr (son nom de fichier) afin que je sache de quel fichier il provient. Comme ça:
/opt/minecraft/wonders/logs/2018-08-18-2.log.gz:
06:17:05: Starting minecraft server version 1.10.2
06:18:21: Loading properties
06:18:21: Default game type: SURVIVAL
06:18:21: Generating keypair
06:18:21: Starting Minecraft server on *:25565
06:18:22: Using default channel type
Voici la commande que je suis en train d'utiliser avec grep:
egrep -iv "^\[.*(stopping|starting|saving|keep|spawn|joined|lost|left|: (<.*>|\/)|\/(help|forge))
Son but est de supprimer certaines lignes des fichiers journaux tout en laissant celles qui sont pertinentes.
Voici un moyen simple de faire cela, supposons cette commande:
ls -d Documents/ NotExist/ /dev/null
la sortie est:
ls: cannot access 'NotExist/': No such file or directory
/dev/null Documents/
la première ligne est stderr
et la seconde est stdout
, si je dirige la sortie vers xargs
je peux imprimer une nouvelle ligne après stderr
, puis imprimer le stdout
:
ls -d Documents/ NotExist/ /dev/null | xargs echo -e '\n'
ce qui me donne:
ls: cannot access 'NotExist/': No such file or directory
/dev/null Documents/
C'est possible avec la duplication de descripteurs de fichiers. Récit long, bref: pipe stderr, où il peut être traité, et envoyez stdin au terminal de contrôle (pour abréger, il s'agit de /dev/tty
), de sorte qu'il s'affiche toujours à l'écran:
$ stat /etc/passwd noexist 2>&1 > /dev/tty | while IFS= read -r line; do echo "---"; echo "$line"; echo "---"; done
File: /etc/passwd
Size: 2380 Blocks: 8 IO Block: 4096 regular file
Device: 801h/2049d Inode: 937653 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2018-08-20 13:05:01.171247259 +0800
Modify: 2018-07-24 04:51:36.799057737 +0800
Change: 2018-07-24 04:51:36.867020035 +0800
Birth: -
---
stat: cannot stat 'noexist': No such file or directory
---
Ici, je sépare stderr avec la ligne ---
, mais vous voyez l’idée.
Si vous souhaitez les séparer visuellement, vous pouvez également colorier le résultat. Basé sur réponse de Balazs Pozar , vous pourriez faire:
command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)
Pour résoudre le problème d'édition, si vous devez filtrer stdout et stderr avec grep
(par exemple, si vous recherchez un modèle dans stdout
, mais un autre modèle dans stderr
), vous utilisez plusieurs substitutions de processus >()
:
gunzip -vc /opt/minecraft/wonders/logs/20* > >(grep 'stdin pattern' ) 2> >(grep 'stderrpattern' )
En adressant les commentaires, si vous avez seulement besoin de séparer le nom de fichier /opt/minecraft/wonders/logs/2017-08-28-2.log.gz:
du contenu ultérieur, vous pouvez utiliser sed
:
gunzip -vc /opt/minecraft/wonders/logs/20* |
sed -r 's/^(\/opt\/minecraft\/wonders\/logs\/2018-08-18-2.log.gz:) *(.*)$/\1\n\2/'
Tester:
$ echo /opt/minecraft/wonders/logs/2018-08-18-2.log.gz: 06:17:05: Starting minecraft server version 1.10.2 | sed -r 's/^(\/opt\/minecraft\/wonders\/logs\/2018-08-18-2.log.gz:) *(.*)$/\1\n\2/'
/opt/minecraft/wonders/logs/2018-08-18-2.log.gz:
06:17:05: Starting minecraft server version 1.10.2
Je pense que ce qui se passe réellement, c’est que stdout est en train de devenir mélangé à stderr, parce que le premier est mis en tampon de ligne et le dernier est non tampon par défaut. Pour illustrer:
$ gunzip -vc ../foo.txt.gz
../foo.txt.gz: some text here
more text here
26.7%
(notez que -v
provoque l’émission de deux informations sur stderr: le nom de fichier et le% compression - avec une sortie standard située entre les deux).
Si vous mettez également stderr en mémoire tampon, les flux apparaîtront sur des lignes distinctes:
$ stdbuf -eL gunzip -vc ../foo.txt.gz
some text here
more text here
../foo.txt.gz: 26.7%
mais pas nécessairement dans l'ordre requis - vous pouvez essayer d'utiliser l'utilitaire sponge
pour absorber tous de stdout avant de le sortir, afin que la sortie stderr (nomfichier et%) apparaisse dans le terminal d'abord:
gunzip -vc ../foo.txt.gz | sponge
../foo.txt.gz: 26.7%
some text here
more text here