Как сделать труб работы со средой выполнения.exec ()?


рассмотрим следующий код:

String commandf = "ls /etc | grep release";

try {

    // Execute the command and wait for it to complete
    Process child = Runtime.getRuntime().exec(commandf);
    child.waitFor();

    // Print the first 16 bytes of its output
    InputStream i = child.getInputStream();
    byte[] b = new byte[16];
    i.read(b, 0, b.length); 
    System.out.println(new String(b));

} catch (IOException e) {
    e.printStackTrace();
    System.exit(-1);
}
программы:
/etc:
adduser.co

когда я бегу от оболочки, конечно, он работает так, как ожидалось:

poundifdef@parker:~/rabbit_test$ ls /etc | grep release
lsb-release

интернет говорит мне, что из-за того, что поведение трубы не является кросс-платформенным, блестящие умы, которые работают на фабрике Java, производящей Java, не могут гарантировать, что трубы работают.

как я могу это сделать?

Я не собираюсь делать весь мой разбор с помощью Java конструкции, а не grep и sed, потому что если я хочу изменить язык, я буду вынужден переписать парсинг кода на этом языке, который совершенно не подходит.

как я могу заставить Java выполнять конвейер и перенаправление при вызове команд оболочки?

3 90

3 ответа:

написать сценарий и выполнить сценарий вместо отдельных команд.

Труба является частью оболочки, так что вы также можете сделать что-то вроде этого:

String[] cmd = {
"/bin/sh",
"-c",
"ls /etc | grep release"
};

Process p = Runtime.getRuntime().exec(cmd);

я столкнулся с аналогичной проблемой в Linux, за исключением того, что это был "ps-ef | grep someprocess".
По крайней мере, с "ls" у вас есть независимая от языка (хотя и более медленная) замена Java. Например.:

File f = new File("C:\");
String[] files = f.listFiles(new File("/home/tihamer"));
for (String file : files) {
    if (file.matches(.*some.*)) { System.out.println(file); }
}

С " ps " Это немного сложнее, потому что Java, похоже, не имеет API для этого.

Я слышал, что сигар может помочь нам: https://support.hyperic.com/display/SIGAR/Home

самое простое решение, однако, (как указал Кай) выполнить команду piped в виде строкового массива. Вот полный код:

try {
    String line;
    String[] cmd = { "/bin/sh", "-c", "ps -ef | grep export" };
    Process p = Runtime.getRuntime().exec(cmd);
    BufferedReader in =
            new BufferedReader(new InputStreamReader(p.getInputStream()));
    while ((line = in.readLine()) != null) {
        System.out.println(line); 
    }
    in.close();
} catch (Exception ex) {
    ex.printStackTrace();
}

что касается того, почему массив строк работает с трубой, а одна строка-нет... это одна из тайн Вселенной (особенно если вы не читали исходный код). Я подозреваю, что это потому, что когда exec дается одна строка, он сначала анализирует ее (таким образом, что нам не нравится). Напротив, когда exec получает массив строк, он просто передает его в операционную систему без разбора оно.

на самом деле, если мы возьмем время из напряженного дня и посмотрим на исходный код (at http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Runtime.java#Runtime.exec%28java.lang.String%2Cjava.lang.String[]%2Cjava.io.File%29), мы находим, что именно это и происходит:

public Process  [More ...] exec(String command, String[] envp, File dir) 
          throws IOException {
    if (command.length() == 0)
        throw new IllegalArgumentException("Empty command");
    StringTokenizer st = new StringTokenizer(command);
    String[] cmdarray = new String[st.countTokens()];
    for (int i = 0; st.hasMoreTokens(); i++)
        cmdarray[i] = st.nextToken();
    return exec(cmdarray, envp, dir);
}

создать среду для запуска каждого процесса. Получите OutputStream из первой среды выполнения и скопируйте его в InputStream из второй.