【问题标题】:One does not simply grep into ProcessBuilder一个不简单的 grep 进入 ProcessBuilder
【发布时间】:2025-12-28 16:15:10
【问题描述】:

有谁知道如何将 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"

我终于尝试在shell执行命令并得到:

sing

虽然出于某种原因它是红色字体。那我做错了什么?

【问题讨论】:

  • 在您的示例中,grep 采用一个参数(sing),&lt;&lt;&lt; 符号告诉 shell 将后面的任何内容作为输入流传递给 grep。您的 ProcessBuilder 代码将输入流符号视为另一个参数。

标签: java shell grep processbuilder herestring


【解决方案1】:

我做错了什么?

很明显的东西。

您是否期望execve() 了解shell 构造?没有。

好吧,您也不应该期望ProcessBuilder 理解这些。尽管它没有execve() 低级别,但它的级别足够低,以至于命令的参数是“原始的”。因此,在您的命令中,&lt;&lt;&lt;"sing" 按原样作为参数传递给grep;这意味着grep 将其视为要读取的文件。

记住这一点:您在 shell 中输入的内容由 shell 解释ProcessBuilder 不会使用 shell 来执行其进程,execve() 也不会。反过来,这意味着您不能使用 shell 构造。

如果你想grep 你必须feed your process' input 提供你想要的文字。但是,当 Java 具有内置的正则表达式引擎时,为什么要使用 grep 是另一个问题,当然。

至于:

虽然由于某种原因它是红色字体

它只是来自grep 命令的文本修饰(嗯,至少是 GNU grep);查看其手册页和--color 选项。简而言之,在您的情况下,它检测到您的 tty 具有更改文本颜色的能力,并使用它来装饰匹配的文本。

试试看:

echo foobar | grep foo

它将以红色回显foobarfoo

【讨论】:

  • 对于回答者,尽管您发布了一个正确的答案,但您对 OP 的傲慢或粗鲁是不必要的,而忽略了这样一个事实,即对您来说似乎显而易见的事情对提问者来说可能是一个谜,如果他知道那是什么问的意义何在?
  • @qualebs 好的,我同意,我可以改写我的答案,但让我感到不安的是,这是 OS 编程 101。这种(非)微妙之处仍然没有在 2017 年的编程课程中教授让我困惑
【解决方案2】:

您实际上可以使用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

【讨论】:

  • 虽然有可能,但几乎从来都不是正确的做法;可以说,它与臭名昭著的“system()”处于同一水平……
  • 好吧,这仍然不会阻止程序员使用system(),而且公平地说,这样做的有效案例很少(虽然不是这个grep
  • 这似乎是一个有用的方法。另外@fge 假设您指的是C“system()”函数,为什么它如此臭名昭著?您可能希望分享任何链接吗?