Sous Windows, puis-je rediriger stdout vers un canal (nommé) en ligne de commande?

17

Existe-t-il un moyen de rediriger la sortie standard d'un processus dans la console Win32 vers un canal nommé ? Les canaux nommés sont intégrés à Windows et bien qu'ils soient un concept utile, je ne les ai jamais vus utilisés depuis la ligne de commande.

C'est à dire. comme example.exe >\\.\mypipe. (Cette syntaxe n'est peut-être pas correcte mais vous obtenez le point.) Je voudrais pouvoir rediriger stdout et stderr vers différents tuyaux en même temps.

Je voudrais éviter d'utiliser des fichiers physiques comme substitut, pour éviter de traiter la lenteur d'E / S, les tampons d'E / S, les verrous de fichiers, les droits d'accès, l'espace disponible sur le disque dur, la décision d'écraser, la persistance non voulue, etc.

Une autre raison est que l' ensemble d'outils Windows traditionnel n'est pas conçu autour d'une philosophie basée sur des fichiers (texte) autant que sous Unix . De plus, les tuyaux nommés ne pouvaient pas être facilement montés dans Windows, voire pas du tout.

Enfin, il y a la curiosité si un bon concept pouvait être mis à profit.

n611x007
la source
1
Vous voulez dire comme rediriger vers un canal nommé ou un emplacement de courrier? Avez-vous / êtes-vous prêt à écrire l'extrémité de réception?
ixe013
Oui, comme pour une pipe ou un emplacement de courrier nommé. Je n'ai pas encore mais je veux écrire la fin de la réception.
n611x007

Réponses:

8

Je ne sais pas pourquoi vous ne voulez pas rediriger vers un fichier. Il y a deux méthodes que je vais fournir ici. Une méthode consiste à rediriger vers et à lire un fichier, l'autre est un ensemble de programmes.


Tuyaux nommés

J'ai écrit deux programmes pour .NET 4. L'un envoie la sortie à un canal nommé, l'autre lit à partir de ce canal et s'affiche sur la console. L'utilisation est assez simple:

asdf.exe | NamedPipeServer.exe "APipeName"

Dans une autre fenêtre de console:

NamedPipeClient.exe "APipeName"

Malheureusement, cela ne peut que rediriger stdout(ou stdin, ou combiné), pas stderrseul, en raison des limitations de l'opérateur de canal ( |) dans l'invite de commandes Windows. Si vous savez comment envoyer stderrvia cet opérateur de tuyau, cela devrait fonctionner. Alternativement, le serveur pourrait être modifié pour lancer votre programme et rediriger spécifiquement stderr. Si cela est nécessaire, faites-le moi savoir dans un commentaire (ou faites-le vous-même); ce n'est pas trop difficile si vous avez des connaissances en bibliothèque C # et .NET "Process".

Vous pouvez télécharger le serveur et le client .

Si vous fermez le serveur après la connexion, le client se fermera immédiatement. Si vous fermez le client après la connexion, le serveur se fermera dès que vous tenterez d'envoyer quelque chose par son intermédiaire. Il n'est pas possible de reconnecter un tuyau cassé, principalement parce que je ne peux pas être dérangé de faire quelque chose de si compliqué en ce moment. Il est également limité à un client par serveur .

Code source

Celles-ci sont écrites en C #. Il n'y a pas grand-chose à essayer de l'expliquer. Ils utilisent le .NET NamedPipeServerStream et NamedPipeClientStream .

Le serveur:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Pipes;
using System.IO;

namespace NamedPipeServer
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args == null || args.Length == 0)
            {
                Console.Error.WriteLine("[NamedPipeServer]: Need pipe name.");
                return;
            }

            NamedPipeServerStream PipeServer = new NamedPipeServerStream(args[0], System.IO.Pipes.PipeDirection.Out);
            PipeServer.WaitForConnection();
            StreamWriter PipeWriter = new StreamWriter(PipeServer);
            PipeWriter.AutoFlush = true;

            string tempWrite;

            while ((tempWrite = Console.ReadLine()) != null)
            {
                try
                {
                    PipeWriter.WriteLine(tempWrite);
                }
                catch (IOException ex)
                {
                    if (ex.Message == "Pipe is broken.")
                    {
                        Console.Error.WriteLine("[NamedPipeServer]: NamedPipeClient was closed, exiting");
                        return;
                    }
                }
            }

            PipeWriter.Close();
            PipeServer.Close();
        }
    }
}

Le client:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Pipes;
using System.IO;

namespace NamedPipeClient
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args == null || args.Length == 0)
            {
                Console.Error.WriteLine("[NamedPipeClient]: Need pipe name.");
                return;
            }

            NamedPipeClientStream PipeClient = new NamedPipeClientStream(".", args[0], System.IO.Pipes.PipeDirection.In);
            PipeClient.Connect();
            StreamReader PipeReader = new StreamReader(PipeClient);

            string tempRead;

            while ((tempRead = PipeReader.ReadLine()) != null)
            {
                Console.WriteLine(tempRead);
            }

            PipeReader.Close();
            PipeClient.Close();
        }
    }
}

Redirection vers un fichier

type NUL>StdErr.temp
start powershell -c Get-Content StdErr.temp -Wait
MyExecutable.exe 2>StdErr.temp
  1. Créer un fichier vide
  2. Démarrer une nouvelle fenêtre de console qui surveille le fichier
  3. Exécutez l'exécutable et redirigez la stderrsortie vers ce fichier

Cela fournit l'effet souhaité d'une fenêtre de console à regarder stdout(et fournir stdin) et d'une autre à regarderstderr .

Tout ce qui imite tailfonctionnerait. La méthode PowerShell fonctionne de manière native dans Windows, mais peut être un peu lente (c'est-à-dire qu'il y a une certaine latence entre l'écriture dans le fichier et l'affichage à l'écran). Voir cette question StackOverflow pour d'autres tailalternatives.

Le seul problème est que le fichier temporaire peut devenir assez volumineux. Une solution de contournement possible consiste à exécuter une boucle qui s'imprime uniquement si le fichier a du contenu et à effacer le fichier immédiatement après, mais cela provoquerait une condition de concurrence critique.

Bob
la source
2
Les utilisateurs UNIX sont ceux qui sont ennuyés parce que Windows ne parvient pas encore à implémenter une idée vieille de 40 ans de manière sensée. Vous ne devriez pas avoir besoin d'écrire un programme personnalisé chaque fois que vous voulez faire une chose de base. facepalm
bambams
Voir ci-dessous: vous pouvez utiliser le chemin UNC affecté à un canal nommé et y accéder directement.
Erik Aronesty
15

Je suis surpris que cela n'ait pas déjà été répondu correctement. Il existe en effet un chemin UNC assigné aux tubes nommés par le système, accessible sur n'importe quelle machine du réseau, qui peut être utilisé comme un fichier normal:

program.exe >\\.\pipe\StdOutPipe 2>\\.\pipe\StdErrPipe

En supposant que des canaux nommés "StdOutPipe" et "StdErrPipe" existent sur cette machine, cela tente de s'y connecter et d'y écrire. La pipepièce est ce qui spécifie que vous voulez un tuyau nommé.

IllidanS4 veut récupérer Monica
la source
Je pense que cela devrait être marqué comme la bonne réponse, car c'est juste ce qui demande @ n611x007 que des programmes externes ne sont pas nécessaires pour ce faire!
arturn
le problème est que vous devez toujours lancer un service quelconque qui crée ces canaux .... ils n'existent pas indépendamment d'un programme créé et sont détruits lorsque le programme disparaît.
Erik Aronesty
@ErikAronesty J'ai supposé que ces tuyaux existaient déjà. Sinon, il est impossible de les créer uniquement avec cmd.exe.
IllidanS4 veut que Monica revienne
Oui, c'est le truc cool avec les pipes Unix, vous pouvez créer des pipes depuis la ligne de commande
Erik Aronesty
1

Pas avec le shell standard (CMD.EXE). Pour les programmeurs, c'est assez simple . Saisissez simplement les deux tuyaux d'un processus que vous avez commencé.

MSalters
la source
1
Seul prb est que l'exemple utilise des canaux anonymes, qui ne prennent pas en charge io (async) superposés et sont donc sujets à des blocages, ou au moins PeekNamedPipe doit être utilisé.
Fernando Gonzalez Sanchez
1
Les attentes de blocage ne sont pas des blocages et le problème fondamental (le thread consommateur de données empêche le thread producteur de le produire) n'est pas résolu de toute façon par les E / S superposées.
MSalters
Je voulais dire ceci: blogs.msdn.com/b/oldnewthing/archive/2011/07/07/10183884.aspx , un exemple de blocage.
Fernando Gonzalez Sanchez
1
@FernandoGonzalezSanchez: À peu près le même problème. Notez que la solution recommandée (thread supplémentaire) évite tout besoin d'E / S asynchrones.
MSalters
1
Oui, le prb dans l'exemple msdn est que le parent est bloqué pour toujours, dans la fonction ReadFromPipe, ligne bSuccess = ReadFile (g_hChildStd_OUT_Rd, chBuf, BUFSIZE, & dwRead, NULL); lira 70 octets la première fois, et la deuxième fois restera bloqué pour toujours (PeekNamedPipe, qui est manquant, n'est pas bloquant).
Fernando Gonzalez Sanchez
-1

Vos préférences pour un canal de données Windows depuis un serveur vers une fenêtre DOS client immédiatement ou plus tard peuvent être satisfaites avec un petit lecteur RAM. La même mémoire est allouée pour les données, écrite / lue avec un nom de type système de fichiers. Le client supprime le fichier épuisé et attend un autre, ou le laisse disparaître lorsque l'ordinateur s'arrête.

user999850
la source