2009-09-02 20 views
4

J'ai besoin d'un appel de fonction "système", identique à ceux de Python, Perl, PHP, Ruby, & c. Ce sera un composant d'une bibliothèque standard JavaScript appelée Narwhal, lorsqu'elle est exécutée sur le moteur JavaScript de Rhino, qui est à son tour exécuté sur Java.Implémentation de la commande "système" en Java

Le problème est que la bibliothèque standard de Java semble avoir éliminé la possibilité de générer un sous-processus qui partage le stdio du processus parent. Cela signifie que vous ne pouvez pas reporter l'interactivité au sous-processus.

Ma première fissure à ce sujet était d'implémenter subprocess.popen de Python. Cela utilise trois threads "pumper" pour copier activement le stdio du processus parent indépendamment (pour éviter les interblocages). Malheureusement, cela nous pose deux problèmes. Premièrement, l'entrée ne se ferme pas automatiquement lorsque le sous-processus se termine volontairement. Deuxièmement, les flux vers le processus enfant ne sont pas tamponnés et ne s'affichent pas correctement.

Je cherche des solutions qui feraient fonctionner notre commande require ("os"). System() comme on pourrait s'y attendre.

Le projet est à http://narwhaljs.org

Code pertinent:

Répondre

2

Je ne sais pas si c'est ce que vous cherchez, mais vous pouvez appeler la fonction C system par la JNA library:

public class System { 
    public interface C extends Library { 
    C INSTANCE = (C) Native.loadLibrary(
     (Platform.isWindows() ? "msvcrt" : "c"), C.class); 

    public int system(String format); 
    } 

    public static void main(String[] args) { 
    C.INSTANCE.system("vi"); 
    } 
} 

test Superficielle travaillé sur Windows, de toute façon.

+0

Ajout de jni.jar, ce peu de JavaScript a fait l'affaire. Je vous remercie! var jna = Packages.com.sun.jna; var clib = jna.NativeLibrary.getInstance (jna.Platform.isWindows()? "Msvcrt": "c"); var csystem = clib.getFunction ("système"); csystem.invoke (["echo Bonjour, monde!"]); http://gist.github.com/181225 –

1

Vous devez surveiller la sortie d'état de processus séparément, soit en interrogeant le code de sortie ou en ayant un thread séparé en attente dans la méthode Process.waitFor().

En ce qui concerne le tamponnage et le rinçage des flux, je ne pense pas qu'il existe une solution facile. Il y a plusieurs classes Java qui tamponnent sous différentes formes (BufferedInputStream, etc.). Peut-être que l'un d'eux peut aider?

+0

@Joachim: merci pour les liens ajoutés! – JesperE

1

Si je vous comprends bien, vous voulez quelque chose comme ça:

import java.util.*; 
import java.io.*; 
class StreamGobbler extends Thread 
{ 
    InputStream is; 
    String type; 
    OutputStream os; 

    StreamGobbler(InputStream is, String type) 
    { 
     this(is, type, null); 
    } 
    StreamGobbler(InputStream is, String type, OutputStream redirect) 
    { 
     this.is = is; 
     this.type = type; 
     this.os = redirect; 
    } 

    public void run() 
    { 
     try 
     { 
      PrintWriter pw = null; 
      if (os != null) 
       pw = new PrintWriter(os); 

      InputStreamReader isr = new InputStreamReader(is); 
      BufferedReader br = new BufferedReader(isr); 
      String line=null; 
      while ((line = br.readLine()) != null) 
      { 
       if (pw != null) 
        pw.println(line); 
       System.out.println(type + ">" + line);  
      } 
      if (pw != null) 
       pw.flush(); 
     } catch (IOException ioe) 
      { 
      ioe.printStackTrace(); 
      } 
    } 
} 
public class GoodWinRedirect 
{ 
    public static void main(String args[]) 
    { 
     if (args.length < 1) 
     { 
      System.out.println("USAGE java GoodWinRedirect <outputfile>"); 
      System.exit(1); 
     } 

     try 
     {    
      FileOutputStream fos = new FileOutputStream(args[0]); 
      Runtime rt = Runtime.getRuntime(); 
      Process proc = rt.exec("java jecho 'Hello World'"); 
      // any error message? 
      StreamGobbler errorGobbler = new 
       StreamGobbler(proc.getErrorStream(), "ERROR");    

      // any output? 
      StreamGobbler outputGobbler = new 
       StreamGobbler(proc.getInputStream(), "OUTPUT", fos); 

      // kick them off 
      errorGobbler.start(); 
      outputGobbler.start(); 

      // any error??? 
      int exitVal = proc.waitFor(); 
      System.out.println("ExitValue: " + exitVal); 
      fos.flush(); 
      fos.close();   
     } catch (Throwable t) 
      { 
      t.printStackTrace(); 
      } 
    } 
} 

J'ai trouvé ce morceau de code: JavaWorld il y a quelque temps quand je cherchais une solution similaire à un système envelopper les appels à certains fichiers exe.

Mon code a un peu évolué depuis, mais je pense que c'est un bon exemple.

+0

+1 pour la consommation simultanée –

0

Il est vraiment important de consommer la sortie standard du processus et l'erreur simultanément. Voir l'exemple de code de Carlos Tasada ailleurs ici. Si ce n'est pas le cas, votre code peut (ou peut ne pas fonctionner) en fonction de la sortie de votre processus engendré. Au fur et à mesure que cette sortie change (si votre processus engendré rencontre une erreur, par exemple), sans la consommation simultanée, votre processus sera bloqué. La plupart des problèmes que je vois sur SO liés à Process.exec() sont liés au blocage.