Как проверить наличие программы на пути

Я пишу программу в scala, которая вызывает:

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

Я хочу проверить, доступно ли "svn" из командной строки (т.е. доступно в PATH). Как я могу это сделать?

PS: Моя программа предназначена для работы в Windows

Ответы

Ответ 1

Я не программист scala, но то, что я хотел бы сделать на любом языке, - это выполнить что-то вроде "svn help", чтобы проверить код возврата (0 или 1) метода exec... if он не работает, svn не находится в пути: P

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

По соглашению, значение 0 указывает на нормальное завершение.

Ответ 2

Возможно, кому-то будет интересно решение 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)));

Кстати, вы можете заменить anyMatch(...) на filter(...).findFirst() - так что вы получите точный исполняемый путь.

Ответ 3

Selenium имеет достаточно реалистичную реализацию для Windows/Linux/Mac в классе org.openqa.selenium.os.ExecutableFinder, с public доступ с Selenium 3.1 (ранее доступный только через устаревший метод org.openqa.selenium.os.CommandLine#find). Это ASL 2.0, хотя.

Обратите внимание, что ExecutableFinder не понимает PATHEXT в Windows - он просто имеет жестко запрограммированный набор исполняемых расширений файлов (.exe,.com,.bat).

Ответ 4

этот код использует команду "where" в Windows и "какая" команда в других системах, чтобы проверить, знает ли система о желаемой программе в PATH. Если найдено, функция возвращает java.nio.file.Path в программу, а null - в противном случае.

Я тестировал его с помощью Java 8 на Windows 7 и 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");
    }
}

Чтобы использовать его:

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

В моей системе Windows 7 отображается:

C:\Windows\System32\notepad.exe

И на linux:

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

/USR/бен/PSQL

Преимущество этого метода заключается в том, что он должен работать на любой платформе, и нет необходимости анализировать переменную среды PATH или просматривать реестр. Желаемая программа никогда не вызывается, даже если она найдена. Наконец, нет необходимости знать расширение программы. gnuplot.exe под Windows и gnuplot под Linux будут найдены одним и тем же кодом:

    SimulationUtils.lookForProgramInPath("gnuplot")

Предложения по улучшению приветствуются!

Ответ 5

Что касается исходного вопроса, я бы также проверял существование, как предположил FMF.

Я также хотел бы указать, что вам придется обрабатывать хотя бы выходные данные процесса, читая доступные данные, чтобы потоки не были заполнены до краев. Это приведет к блокировке процесса.

Чтобы сделать это, извлеките InputStreams процесса, используя proc.getInputStream() (для System.out) и proc.getErrorStream() (для System.err) и прочитайте доступные данные в разных потоках.

Я просто скажу вам, потому что это обычная ошибка, и svn потенциально может создать довольно много выходных данных, поэтому, пожалуйста, не используйте downtote для offtopicness;)

Ответ 6

Если у вас установлен cygwin, вы можете сначала вызвать "which svn", который вернет абсолютный путь к svn, если он находится в исполняемом пути, или "который: no svn in (...)". Вызов "which" вернет exitValue из 1, если не найден, или 0, если он найден. Вы можете проверить этот код ошибки в виде данных FMF.

Ответ 7

По моему опыту невозможно рассказать по различным системам, просто вызывая команду с ProcessBuilder, если она выходит или нет (ни Exceptions, ни возвращаемые значения не кажутся непротиворечивыми)

Итак, вот решение Java7, которое пересекает переменную среды PATH и ищет соответствующий инструмент. Будет проверять все файлы, если каталог. matchesExecutable должно быть именем инструмента, игнорирующего расширение и регистр.

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;
}

Вот помощник:

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;
}

Ответ 8

Это похоже на ответ Dmitry Ginzburg, но также учитывает редкий случай, когда кто-то имеет неверный путь в переменной среды PATH. Это может привести к InvalidPathException.

private static final String ENVIRONMENT_VARIABLES_TEXT = System.getenv("PATH");

private static boolean isCommandAvailable(String executableFileName)
{
    String[] environmentVariables = ENVIRONMENT_VARIABLES_TEXT.split(File.pathSeparator);
    for (String environmentVariable : environmentVariables)
    {
        try
        {
            Path environmentVariablePath = Paths.get(environmentVariable);
            if (Files.exists(environmentVariablePath))
            {
                Path resolvedEnvironmentVariableFilePath = environmentVariablePath.resolve(executableFileName);
                if (Files.isExecutable(resolvedEnvironmentVariableFilePath))
                {
                    return true;
                }
            }
        } catch (InvalidPathException exception)
        {
            exception.printStackTrace();
        }
    }

    return false;
}

В целом это может быть наиболее эффективным и надежным решением.