12
votes

Capture de sortie de wshshell.exec à l'aide de l'hôte de script Windows

J'ai écrit les deux fonctions suivantes et appelez la seconde ("callandwait") de JavaScript exécutant à l'intérieur de l'hôte de script Windows. Mon intention globale est d'appeler un programme de ligne de commande d'un autre. C'est-à-dire que je suis en train d'exécuter les scripts initiaux à l'aide de cscript, puis d'essayer d'exécuter quelque chose d'autre (fourmi) à partir de ce script.

function readAllFromAny(oExec)
{
     if (!oExec.StdOut.AtEndOfStream)
          return oExec.StdOut.ReadLine();

     if (!oExec.StdErr.AtEndOfStream)
          return "STDERR: " + oExec.StdErr.ReadLine();

     return -1;
}

// Execute a command line function....
function callAndWait(execStr) {
 var oExec = WshShell.Exec(execStr);
  while (oExec.Status == 0)
 {
  WScript.Sleep(100);
  var output;
  while ( (output = readAllFromAny(oExec)) != -1) {
   WScript.StdOut.WriteLine(output);
  }
 }

}


1 commentaires

Je ne pense pas qu'il y ait un moyen de faire cela, je cherchais un moment moi-même. Ma "solution" est d'avoir une fonction qui repend une commande qui définit les variables d'environnement requises chaque fois qu'une commande est appelée, comme dans wshell.exec ("% comspec% setenvars.bat & réal_program.exe"). Assez maladroit.


5 Réponses :


1
votes

Oui, la fonction EXED semble être brisée en matière de sortie terminale.

J'ai utilisé une fonction similaire ConsumestD (E) {wscript.stdout.write (e.stdout.readall ()); wscript.sderr.write (e.sderr.readall ());} que j'appelle dans une boucle similaire à la vôtre. Je ne sais pas si la vérification de la ligne EOF et de lecture par ligne est meilleure ou pire.


1 commentaires

Merci, j'ai eu le même problème. Cette approche semble être la seule qui fonctionne.



2
votes

Tout d'abord, votre boucle est cassée en ce sens qu'elle tente toujours de lire depuis oexec.stdout d'abord. S'il n'y a pas de sortie réelle, il sera suspendu jusqu'à ce qu'il y soit. Vous ne verrez aucun starr de sortie jusqu'à stdout.Atendofstream devient vrai (probablement lorsque l'enfant se termine). Malheureusement, il n'y a pas de concept d'E / S non bloquant dans le moteur de script. Cela signifie appeler lire et le recevoir immédiatement s'il n'y a pas de données dans le tampon. Ainsi, il n'y a probablement aucun moyen de faire fonctionner cette boucle comme vous le souhaitez. Deuxièmement, wshell.run ne fournit aucune propriété ni méthodes pour accéder aux E / S standard du processus d'enfant. Il crée l'enfant dans une fenêtre séparée, totalement isolée du parent, à l'exception du code de retour. Cependant, si tout ce que vous voulez, c'est pouvoir voir la sortie de l'enfant, cela pourrait être acceptable. Vous pourrez également interagir avec l'enfant (entrée) mais uniquement via la nouvelle fenêtre (voir SendKeys ).

Quant à l'utilisation Readall () , cela serait encore pire car il collecte toutes les entrées du flux avant de revenir afin que vous ne voyiez rien du tout avant la fermeture du ruisseau. Je ne sais pas pourquoi le exemple Place le Readall dans une boucle qui construit une chaîne, un seul si (! wscript.stdin.atendofstream) devrait être suffisant pour éviter les exceptions.

Une autre alternative peut être d'utiliser les méthodes de création de processus dans WMI. La manière dont les E / S standard sont traités ne sont pas claires et il ne semble pas être un moyen d'attribuer des flux spécifiques comme stdin / out / ERR . Le seul espoir serait que l'enfant hériterait de ceux-ci du parent, mais c'est ce que vous voulez, n'est-ce pas? (Ce commentaire basé sur une idée et un peu de recherche mais pas de test réel.)

Fondamentalement, le système de script n'est pas conçu pour une communication / synchronisation interprocessée compliquée.

Remarque: les tests confirmant ce qui précède ont été effectués sur Windows XP SP2 à l'aide de la version 5.6 de script. Référence aux manuels actuels (5.8) suggère aucun changement.


0 commentaires

4
votes

Une autre technique qui pourrait aider dans cette situation est de rediriger le flux d'erreur standard de la commande pour accompagner la sortie standard. Faites cela en ajoutant "% ComSpec% / C" à l'avant et "2> & 1" à la fin de la chaîne Execstr. C'est-à-dire, changez la commande que vous exécutez de: xxx

à: xxx

"2> & 1" est une instruction redirection qui provoque La sortie STDERR (descripteur de fichier 2) à écrire dans le flux STDOUT (descripteur de fichier 1). Vous devez inclure la partie "% ComSpec% / C" car c'est l'interpréteur de commande qui comprend la redirection de la ligne de commande. Voir http://technet.microsoft.com/en-us/library/ee156605. Aspx
L'utilisation de "% ComSpec%" au lieu de "CMD" donne une portabilité à une plage plus large de versions Windows. Si votre commande contient des arguments de chaîne cités, il peut être difficile de les obtenir correctement: Les spécifications pour la manière dont CMD gère des citations après "/ c" semble être incomplète.

avec ceci, votre script n'a besoin que de lire le flux STDOUT et recevra à la fois une sortie standard et une erreur standard. J'ai utilisé cela avec "Net Stop wuauserv", qui écrit à STDOUT sur le succès (si le service est en cours d'exécution) et STDERR sur l'échec (si le service est déjà arrêté).


1 commentaires

Voir mon commentaire sur la réponse de Ben sur la redirection et l'utilisation de citations sur les arguments (peut être effectuée si l'exécutable n'a pas de citations)



11
votes

Vous ne pouvez pas lire de cette manière sur STDERR et STDOUT dans le moteur de script, car il n'y a pas d'IO non bloquante en tant que code Master Bob Bob. Si le processus appelé remplit le tampon (environ 4 ko) sur STDERR pendant que vous essayez de lire de stdout ou inversement, vous allez alors vous échapperez / accrocherez. Vous mourrez de faim en attendant que STDOUT et il vous empêchera d'attendre que vous lisez de STDRERR.

La solution pratique consiste à rediriger stardr vers stdout comme ceci: xxx Voici d'autres mots, ce qui est transmis à CreateProcess, c'est ceci: xxx

ceci invoque cmd.exe, qui interprète la ligne de commande. / S / C invoque une règle d'analyse spéciale de sorte que la première et la dernière citation soit désactivée et que le reste utilisé tel est et exécuté par cmd.exe. Donc, cmd.exe exécute ceci: xxx

l'incantation 2> & 1 redirige prog.exe STDROUT . Cmd.exe va propager le code de sortie.

Vous pouvez maintenant réussir en lisant de stdout et en ignorant STDOUT et STDERR.

L'inconvénient est que la sortie STDERR et STDOUT se mélangent. Tant qu'ils sont reconnaissables, vous pouvez probablement travailler avec cela.


0 commentaires

1
votes

Vous avez peut-être touché le problème de l'impasse décrite sur ce Site Microsoft Support .

Une suggestion est de toujours lire à partir de stdout et starr . Vous pouvez modifier readallfromanany à: xxx


0 commentaires