tar à pipe mais garder la sortie verbeuse -v séparée de STDERR

12

Une commande tar normale

tar cvf foo.tar ./foo >foo.out 2>foo.err

a trois flux d'E / S de sortie

  • archiver les données dans foo.tar
  • liste des noms de fichiers vers STDOUT (redirigé vers foo.out)
  • messages d'erreur vers STDERR (redirigé vers foo.err)

Je peux ensuite inspecter foo.err pour les messages d'erreur sans avoir à lire la liste des noms de fichiers.

si je veux faire quelque chose avec les données d'archive (les diriger via netcat ou un programme de compression spécial), je peux utiliser l' -f -option de tar ainsi

tar cvf - ./foo 2>foo.err | squish > foo.tar.S

Mais maintenant, ma liste de noms de fichiers est mélangée à mes messages d'erreur parce que la -vsortie de tar ne peut évidemment pas aller vers STDOUT (c'est là que les données d'archives circulent), donc tar écrit intelligemment cela dans STDERR à la place.

En utilisant le shell Korn, existe-t-il un moyen de construire une commande qui redirige le flux d'archive vers une autre commande tout en capturant la -vsortie séparément des messages d'erreur.

RedGrittyBrick
la source
Connaissez-vous tee? Cela semble être un cas d'utilisation assez valable pour cela.
HalosGhost

Réponses:

9

Si votre système prend en charge /dev/fd/n:

tar cvf /dev/fd/3 ./foo 3>&1 > foo.out 2>foo.err | squish > foo.tar.S

Avec les implémentations AT&T de ksh(ou bashou zsh), vous pourriez écrire en utilisant la substitution de processus :

tar cvf >(squish > foo.tar.S) ./foo > foo.out 2>foo.err

Cela fait exactement la même chose, sauf que cette fois, le shell décide du descripteur de fichier à utiliser au lieu de 3(généralement au-dessus de 9). Une autre différence est que cette fois, vous obtenez le statut de sortie de tarau lieu de squish. Sur les systèmes qui ne prennent pas en charge /dev/fd/n, certains shells peuvent recourir à des canaux nommés pour cette fonctionnalité.

Si votre système ne prend pas en charge /dev/fd/nou que votre shell ne peut pas utiliser de canaux nommés pour sa substitution de processus, c'est là que vous devrez traiter les canaux nommés à la main .

Stéphane Chazelas
la source
6

Vous devez utiliser un canal nommé pour cela.

Créez d'abord un dans le dossier:

mkfifo foo.pipe

Utilisez ensuite cette commande:

tar cvf foo.pipe ./foo >foo.out 2>foo.err & cat foo.pipe >foo.tar

Remarque: la catpartie-, peut maintenant aussi être gzipou quoi que ce soit, qui peut lire à partir d'un tuyau:

tar cvf foo.pipe ./foo >foo.out 2>foo.err & gzip -c foo.pipe >foo.tar

Explication:

La sortie est écrit à la conduite de nom ( foo.pipe), où un autre proccess ( cat, gzip, netcat) lit à partir. Vous ne perdez donc pas les canaux stdout / stderr pour plus d'informations.

le chaos
la source
1
Il peut être intéressant de noter les implications de l'utilisation d'un canal nommé . 1) le mieux est de définir un umask 077(ou d'utiliser un répertoire temporaire privé) pour empêcher d'autres processus de lire ou d'écrire dessus (sur de nombreux systèmes, les canaux nommés, comme d'autres fichiers sont créés lisibles par défaut par le monde), 2) vous devez vous assurer le canal nommé n'est utilisé que par chaque instance de votre script (encore une fois, un répertoire temporaire privé unique aide) 3) Cela signifie que vous devez nettoyer par la suite ou s'il est interrompu.
Stéphane Chazelas
1
+1 - J'aime cette réponse. Malheureusement pour moi, il y a une certaine bizarrerie dans les tuyaux nommés sur mon (ancien) système qui les rend peu fiables lorsque j'essaye.
RedGrittyBrick
1
@ StéphaneChazelas - Je pense qu'il peut parfois être plus facile de nettoyer en premier, comme:p="/tmp/pipe$$"; mkfifo "$p"; (read na; cmd[s]...) <>"$p" & (echo;rm "$p"; cmd[s]...) >"$p"
mikeserv