2009-06-01 4 views
13

J'écris un programme scala qui appellent.Comment vérifier l'existence d'un programme dans le chemin

Runtime.getRuntime().exec("svn ...") 

Je veux vérifier si « svn » est disponible à partir de la ligne de commande (il est accessible Sur le chemin). Comment puis-je faire cela?

PS: Mon programme est conçu pour être exécuté sur Windows

Répondre

13

Je ne suis pas programmeur scala, mais ce que je ferais dans toutes les langues, est d'exécuter quelque chose comme « svn help » juste pour vérifier le code de retour (0 ou 1) de la méthode exec ... si elle ne l'est pas svn dans le chemin: P

Runtime rt = Runtime.getRuntime(); 
Process proc = rt.exec("svn help"); 
int exitVal = proc.exitValue(); 

Par convention, la valeur 0 indique une fin normale.

+0

Je pense qu'il serait préférable de le faire "qui svn" au lieu de "svn aide". Il donnera toujours le bon code de retour pour savoir si svn existe dans le chemin, mais si vous réussissez, vous obtiendrez également le chemin complet vers l'exécutable svn. – Apreche

+5

"qui" n'est pas une commande Windows. – EMMERICH

+0

"where" est l'équivalent Windows de "which" –

2

En ce qui concerne la question originale, je vérifierais aussi l'existence comme le suggère la FMF.

Je voudrais également souligner que vous devrez gérer au moins la sortie du processus, en lisant les données disponibles afin que les flux ne soient pas remplis à ras bord. Cela provoquerait le blocage du processus, sinon. Pour ce faire, récupérez les InputStreams du processus en utilisant proc.getInputStream() (pour System.out) et proc.getErrorStream() (pour System.err) et lisez les données disponibles dans différents threads.

Je viens de vous dire parce que c'est un piège commun et svn potentiellement créateur un peu de sortie s'il vous plaît ne pas downvote pour offtopicness;)

0

Si vous avez Cygwin installé, vous pouvez d'abord appeler « qui svn ", qui retournera le chemin absolu à svn s'il se trouve dans le chemin exécutable, ou bien" which: no svn in (...) ". L'appel à "which" retournera une exitValue de 1 si elle n'est pas trouvée, ou 0 si elle est trouvée. Vous pouvez vérifier ce code d'erreur dans les détails de FMF.

13

Peut-être que quelqu'un serait intéressé par la solution Java 8:

String exec = <executable name>; 
boolean existsInPath = Stream.of(System.getenv("PATH").split(Pattern.quote(File.pathSeparator))) 
     .map(Paths::get) 
     .anyMatch(path -> Files.exists(path.resolve(exec))); 

Par ailleurs, vous pouvez remplacer anyMatch(...) avec filter(...).findFirst() - donc vous obtiendrez le chemin exécutable exact.

+0

Vous seriez mieux avec 'Pattern.splitAsStream' ... –

+0

Lorsque j'essaie cela et que je l'exécute dans mon IDE (j'utilise Eclipse), il ne trouve pas un fichier dont le chemin a été ajouté à mon environnement PATH variable. Cependant, lorsque je crée un JRE exécutable à partir de celui-ci et que je l'exécute depuis mon terminal, cela fonctionne. Donc, je soupçonne que mon PATH de l'intérieur de mon IDE n'est pas le même que mon chemin quand j'ouvre un terminal. Comment puis-je contourner cela? Je préfèrerais ne pas faire un Jar exécutable chaque fois que je veux tester le code. Merci! – skrilmps

+0

Je ne reçois pas non plus cela sur Windows. Si j'applique 'exec = java', j'obtiens un faux résultat. Mais si je cours 'where java' de cmd je reçois un coup. – skrilmps

4

Sélénium a ce qui semble être une mise en œuvre complète pour Windows raisonnablement/Linux/Mac dans la classe org.openqa.selenium.os.ExecutableFinder, avec public accès depuis Sélénium 3.1 (auparavant uniquement accessible via la méthode désapprouvée org.openqa.selenium.os.CommandLine#find). C'est ASL 2.0 cependant.

Notez que ExecutableFinder ne comprend pas PATHEXT sur Windows - il a juste un ensemble codé en dur d'extensions de fichiers exécutables (.exe, .com, .bat).

+1

C'est public maintenant: https://github.com/SeleniumHQ/selenium/blob/master/java/client/src/org/openqa/selenium/os/ExecutableFinder.java#L48 – gouessej

+0

Merci @gouessej. On dirait que cela ne fait pas encore partie d'une version: https://github.com/SeleniumHQ/selenium/blob/selenium-3.0.1/java/client/src/org/openqa/selenium/os/ExecutableFinder.java#L35 – seanf

3

Ce code utilise la commande «where» sous Windows et la commande «which» sur les autres systèmes pour vérifier si le système connaît le programme souhaité dans PATH. Si trouvé, la fonction retourne un fichier java.nio.file.Path au programme, et null sinon. J'ai testé avec Java 8 sur Windows 7 et Linux Mint 17.3.

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.nio.file.Path; 
import java.nio.file.Paths; 
import java.util.logging.Logger; 


public class SimulationUtils 
{ 
    private final static Logger LOGGER = Logger.getLogger(SimulationUtils.class.getName()); 

    public static Path lookForProgramInPath(String desiredProgram) { 
     ProcessBuilder pb = new ProcessBuilder(isWindows() ? "where" : "which", desiredProgram); 
     Path foundProgram = null; 
     try { 
      Process proc = pb.start(); 
      int errCode = proc.waitFor(); 
      if (errCode == 0) { 
       try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) { 
        foundProgram = Paths.get(reader.readLine()); 
       } 
       LOGGER.info(desiredProgram + " has been found at : " + foundProgram); 
      } else { 
       LOGGER.warning(desiredProgram + " not in PATH"); 
      } 
     } catch (IOException | InterruptedException ex) { 
      LOGGER.warning("Something went wrong while searching for " + desiredProgram); 
     } 
     return foundProgram; 
    } 

    private static boolean isWindows() { 
     return System.getProperty("os.name").toLowerCase().contains("windows"); 
    } 
} 

Pour l'utiliser:

System.out.println(SimulationUtils.lookForProgramInPath("notepad")); 

Sur mon système Windows 7, il affiche:

C: \ Windows \ System32 \ notepad.exe

Et sur Linux:

System.out.println(SimulationUtils.lookForProgramInPath("psql")); 

/usr/bin/psql

L'avantage de cette méthode est qu'elle devrait fonctionner sur toute plate-forme et il n'y a pas besoin d'analyser la variable d'environnement PATH ou consultez le Registre. Le programme désiré n'est jamais appelé, même s'il est trouvé. Enfin, il n'y a pas besoin de connaître l'extension du programme. gnuplot.exe sous Windows et sous Linux gnuplot serait à la fois être trouvé par le même code:

SimulationUtils.lookForProgramInPath("gnuplot") 

suggestions d'amélioration sont les bienvenus!

+0

Cela a effectivement une performance terrible. L'appel de processus externes doit être évité autant que possible. – BullyWiiPlaza

+1

@BullyWiiPlaza: Eh bien, atteindre de bonnes performances n'était pas non plus mon objectif. Si vous avez une idée de comment cela pourrait être plus rapide tout en travaillant sous Windows/Linux/MacOS, n'hésitez pas à suggérer quelque chose. BTW, le résultat pourrait être mis en cache. Enfin, le but est d'appeler un processus externe. Tout appel 'svn' sera probablement beaucoup plus lent que l'appel' where' ou 'which'. –

+0

Oui, la réponse ci-dessus par 'Dmitry Ginzburg' est vraiment bonne. 'where' a un temps de traitement relativement important (peut être plus de 100 - 200ms) et si vous l'appelez plusieurs fois, il devient incontrôlable. Je me suis juste rendu compte dans mon code pourquoi il était sensiblement plus lent. – BullyWiiPlaza

0

Dans mon expérience, il est impossible de dire sur les différents systèmes en appelant simplement une la commande avec le ProcessBuilder si elle sort ou non (ni Exceptions, ni les valeurs de retour semblent cohérentes)

Voici donc une solution java7 que traverse la variable d'environnement PATH et recherche un outil correspondant. Va vérifier tous les fichiers si répertoire. Le matchesExecutable doit être le nom de l'extension et du cas ignorant l'outil.

public static File checkAndGetFromPATHEnvVar(final String matchesExecutable) { 
    String[] pathParts = System.getenv("PATH").split(File.pathSeparator); 
    for (String pathPart : pathParts) { 
     File pathFile = new File(pathPart); 

     if (pathFile.isFile() && pathFile.getName().toLowerCase().contains(matchesExecutable)) { 
      return pathFile; 
     } else if (pathFile.isDirectory()) { 
      File[] matchedFiles = pathFile.listFiles(new FileFilter() { 
       @Override 
       public boolean accept(File pathname) { 
        return FileUtil.getFileNameWithoutExtension(pathname).toLowerCase().equals(matchesExecutable); 
       } 
      }); 

      if (matchedFiles != null) { 
       for (File matchedFile : matchedFiles) { 
        if (FileUtil.canRunCmd(new String[]{matchedFile.getAbsolutePath()})) { 
         return matchedFile; 
        } 
       } 
      } 
     } 
    } 
    return null; 
} 

Voici l'aide:

public static String getFileNameWithoutExtension(File file) { 
     String fileName = file.getName(); 
     int pos = fileName.lastIndexOf("."); 
     if (pos > 0) { 
      fileName = fileName.substring(0, pos); 
     } 
     return fileName; 
} 

public static boolean canRunCmd(String[] cmd) { 
     try { 
      ProcessBuilder pb = new ProcessBuilder(cmd); 
      pb.redirectErrorStream(true); 
      Process process = pb.start(); 
      try (BufferedReader inStreamReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { 
       while ((inStreamReader.readLine()) != null) { 
       } 
      } 
      process.waitFor(); 
     } catch (Exception e) { 
      return false; 
     } 
     return true; 
} 
Questions connexes