2010-09-05 35 views
4

J'ai été sous l'impression que les processus sur le système d'exploitation ont trois flux standard: stdin, stdout, and stderr. J'ai également pensé que les éditeurs de texte comme vim travaillent en prenant des entrées sur stdin et en envoyant des caractères d'échappement ANSI sur stdout. Cependant, mon point de vue de la façon dont les interprètes de ligne de commande ne tient pas dans ce seul cas:Comment les interprètes en ligne de commande fonctionnent-ils?

Quand je lance la commande C:\cygwin\bin\bash.exe, je suis invité avec:

Microsoft Windows [Version 6.1.7600] 
Copyright (c) 2009 Microsoft Corporation. All rights reserved. 

C:\Users\masson>C:\cygwin\bin\bash.exe 
bash-3.2$ 

... mais quand je lance en Java avec l'extrait suivant, le flux stdin est vide:

ProcessBuilder pb = new ProcessBuilder("C:\\cygwin\\bin\\bash.exe"); 
pb.redirectErrorStream(true); 
Process proc = pb.start(); 
final InputStream in = proc.getInputStream(); 

new Thread(new Runnable() { 
    public void run() { 
    // Blocks forever... 
    in.read(new byte[1024]); 
    } 
}).start(); 

ce qui se passe ici? On m'a dit que bash.exe s'exécute en mode interactif. Cela signifie-t-il que les flux standards ne sont pas utilisés? Comment puis-je encore travailler avec ces programmes, et finalement, comment pourrais-je implémenter ma propre version de cmd.exe? Je pense que je ne comprends pas quelque chose de fondamental sur la façon dont les interprètes en ligne de commande fonctionnent ...

(Tous les liens vers des articles traitant de sujets connexes seraient très appréciés.) Je n'ai pas eu beaucoup de chance de chercher. question, les flux standard sont-ils traités différemment dans Windows que dans la plupart des systèmes d'exploitation de type Unix?)

+0

Ce serait intéressant d'écrire un programme interactif CLI Java – TheLQ

+0

Vous devez désactiver la mise en mémoire tampon d'entrée dans votre shell avant d'exécuter le programme Java (et le rallumer après pour que vous puissiez éditer vos commandes). Une obscure commande 'stty' ou quelque chose dont je ne me souviens plus. –

Répondre

1

Etre en mode interactif ne signifie pas que les flux standards ne sont pas utilisés. Mais dans ce cas, Bash s'exécute probablement en non - mode interactif (il détecte qu'il ne parle pas directement à l'application du terminal, donc il suppose qu'il est utilisé par programmation, et n'imprime donc pas la bannière de bienvenue). Dans ce cas, les flux standards sont toujours utilisés, c'est juste que rien n'est sorti.Comme ergosys souligné, vous ne pouvez pas vraiment compter sur in.read(new byte[1024]) retour avant qu'il ait lu le plein 1024 octets, mais il est probablement correct de supposer qu'il le fera - mais il ne reviendra certainement pas avant d'avoir lu au moins un octet, et je pense que c'est le problème ici - vous n'obtenez même pas un octet de sortie. Essayez de passer "-i" à bash pour l'exécuter en mode interactif.

1

Je suis plus un gars Python qu'un gars Java (donc tout ce que je vous dis est des suppositions rapides de JavaDoc), mais il On dirait que vous mettez en place un blocage multi-processus.

in.read(new byte[1024]); ne retournera pas tant qu'il n'aura pas lu 1024 octets de données et que bash.exe ne sortira pas un entier de 1024 octets avant d'arrêter d'attendre l'entrée. (Pour ce faire, utilisez proc.getOutputStream() et nourrir certaines commandes pour répondre à.)

En conséquence, vous obtenez Java en attente de bash pour répondre et bash en attente de Java pour répondre et à la fois parfaitement le contenu d'attendre jusqu'à ce que la mort de l'univers sans s'ennuyer ou fatigué.

Mon conseil est d'utiliser in.available() avant chaque appel à in.read() pour éviter le blocage. De cette façon, vous pouvez basculer entre l'alimentation des données et le retrait sans rester coincé.

En fait, il serait probablement beaucoup plus simple et plus sain de l'emballer dans un BufferedReader.

Mise à jour du commentaire: En outre, lorsque des outils tels que bash détectons que stdin est pas un terminal (voir l'appel système isatty), they buffer en grand (4 Ko ou plus) des morceaux sur l'hypothèse que l'entrée est non interactif. Je ne suis pas sûr si cela va aider, mais essayez de démarrer bash avec le drapeau -i.

+0

Désolé, j'étais en train d'exécuter in.read dans un nouveau thread, mais je l'ai omis par souci de concision. Je vais faire un montage. – peskal

+0

Ahh. Mon diagnostic est toujours vrai dans une certaine mesure. Envoyez-vous des commandes à bash? ... avec des terminaisons de ligne? ... le genre Cygwin sur Windows attend? (Pas sûr de vouloir \ n ou \ r \ n) – ssokolow

+0

De javadoc: "Le nombre d'octets réellement lus est retourné comme un entier.Cette méthode se bloque jusqu'à ce que les données d'entrée soient disponibles, la fin du fichier est détectée, ou une exception Est lancé." J'ai utilisé cette commande sur d'autres programmes, même vim-nox.exe de cygwin, et j'ai toujours reçu une sortie. Pour le problème que vous abordez, voir mon autre question: http://stackoverflow.com/questions/3641407/runtime-getruntime-execc-cygwin-bin-bash-exe-has-no-input-to-read Pour À cette question, je demande seulement si les processus peuvent envoyer des données sur autre chose que stdout et stderr. – peskal

4

Tout programme utilisant la bibliothèque standard c peut indiquer s'il parle à un périphérique tty (une ligne de commande a.k.a) en utilisant la fonction isatty(). Bash détecte probablement qu'il parle à un tuyau au lieu d'un tty et ne génère pas d'invite.

+1

aye: '$ echo tty | bash' cède "pas un tty" – msw