Нельзя просто зайти в ProcessBuilder

кто-нибудь знает, как использовать linux grep с java ProcessBuilder? Почему этот код возвращает пустую строку, когда он должен возвращать «sing»?

import java.io.*;
import java.util.*;

public class Test2 {

public static void main(String[] args) throws InterruptedException,IOException{
    String line;

    // Initiate grep process.
    ProcessBuilder pb = new ProcessBuilder("grep", "\"sing\"", "<<<\"sing\"");
    Process p = pb.start();
    p.waitFor();

    // Get grep output:     
    BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));

    StringBuilder builder = new StringBuilder();
    line = null;
    while ( (line = reader.readLine()) != null) {
        builder.append(line);
        builder.append(System.getProperty("line.separator"));
    }
    String result = builder.toString();
    System.out.println(result);     
}
}

Я также пытаюсь повторить то, что я выполняю с помощью этого кода:

ProcessBuilder pb = new ProcessBuilder("echo","grep", "\"sing\"", "<<<\"sing\"");

и получить правильный результат:

 grep "sing" <<<"sing"

Наконец я пытаюсь выполнить команду в оболочке и получаю:

sing

хотя почему-то красным шрифтом. Так что я делаю неправильно?


person burnedWood    schedule 30.01.2015    source источник
comment
В вашем примере grep принимает один аргумент (sing), а символ <<< указывает оболочке передать все, что следует, в качестве входного потока для grep. Ваш код ProcessBuilder обрабатывает символы входного потока как еще один аргумент.   -  person Brian    schedule 31.01.2015


Ответы (2)


Что я делаю неправильно?

Что-то довольно очевидное.

Ожидаете ли вы, скажем, от execve() понимания конструкций оболочки? Нет.

Ну, вы не должны ожидать, что ProcessBuilder тоже их поймет. Хотя это не такой низкий уровень, как execve(), он достаточно низкий, чтобы аргументы команды были «сырыми». Поэтому в вашей команде <<<"sing" передается как есть в качестве аргумента grep; что означает, что grep рассматривает его как файл для чтения.

Запомните это: то, что вы вводите в оболочке, интерпретируется оболочкой; ProcessBuilder НЕ БУДЕТ использовать оболочку для выполнения своих процессов, равно как и execve(). Что, в свою очередь, означает, что вы не можете использовать конструкции оболочки.

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

As to:

хотя он почему-то красным шрифтом

это просто украшение текста из команды grep (ну, по крайней мере, GNU grep); см. его справочную страницу и параметр --color. Короче говоря, в вашем случае он обнаружил, что ваш tty имеет возможность изменять цвет текста, и он использует это для украшения совпадающего текста.

Попробуйте и:

echo foobar | grep foo

Он будет отображать foobar с foo красным цветом.

person fge    schedule 30.01.2015
comment
Отвечающему, хотя вы разместили правильный ответ, вы были излишне высокомерны или грубы с ОП, игнорируя тот факт, что то, что может показаться вам очевидным, может быть загадкой для спрашивающего, если бы он знал, то какой смысл спрашивать? - person qualebs; 29.05.2017
comment
@qualebs Хорошо, я согласен, я могу перефразировать свой ответ, но что меня нервирует, так это то, что это программирование ОС 101. То, что такого рода (не) тонкости до сих пор не преподаются на курсах программирования в 2017 году, меня сбивает с толку. - person fge; 29.05.2017

На самом деле вы можете запустить ту же команду, используя ProcessBuilder, но вы должны убедиться, что она выполняется bash. Я предпочитаю этот метод утилиты:

public static int runCmd(final String command) {
    Process process=null;
    int ret = 0;
    String[] finalCommand = new String[] { "bash", "-c", command };

    try {
        final ProcessBuilder processBuilder = new ProcessBuilder(finalCommand);
        processBuilder.redirectErrorStream(true);
        process = processBuilder.start();
        ret = process.waitFor();
        // stdout+stderr
        InputStreamReader isr = new InputStreamReader( process.getInputStream() );
        BufferedReader br = new BufferedReader(isr);
        String line;
        while ((line = br.readLine()) != null) {
          System.out.println(line);
        }
        //System.out.println("Program terminated!");
        process.destroy();
        br.close();
        isr.close();
    } 
    catch (IOException|InterruptedException e) { 
        e.printStackTrace(); 
    } 
    return ret;
}

Затем назовите это как:

runCmd("grep -o \"sing\" <<<\"icansing\"");

И это дает мне этот выход:

sing
person anubhava    schedule 30.01.2015
comment
Хотя это возможно, это почти никогда не бывает правильным; она находится на том же уровне дурности, что и пресловутая система()... - person fge; 31.01.2015
comment
Ну, это все еще не остановит программистов, использующих system(), и, честно говоря, есть несколько допустимых случаев этого (но не этот grep) - person anubhava; 31.01.2015
comment
Это кажется полезным методом. Также @fge предполагает, что вы ссылаетесь на функцию C system(), почему она так печально известна? Любые ссылки, которыми вы могли бы поделиться? - person burnedWood; 01.02.2015