web-dev-qa-db-ja.com

Runtime.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構造ではなくgrepsedではなく構成体を使用してすべての解析を行うつもりはありません。言語を変更したい場合は、その言語で解析コードを書き直すことを余儀なくされます。

シェルコマンドを呼び出すときにパイピングとリダイレクトを行うにはどうすればJavaを実行できますか?

97
poundifdef

スクリプトを作成し、個別のコマンドの代わりにスクリプトを実行します。

パイプはシェルの一部であるため、次のようなこともできます。

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

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

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がないようですので、少し難しくなります。

Sigarが私たちを助けることができると聞いたことがあります: https://support.hyperic.com/display/SIGAR/Home

ただし、最も簡単な解決策は(Kajが指摘したように)パイプコマンドを文字列配列として実行することです。完全なコードは次のとおりです。

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

String配列がパイプで機能する理由については、単一の文字列では機能しませんが、それは宇宙の謎の1つです(特にソースコードを読んでいない場合)。 execに単一の文字列が与えられると、最初にそれを解析するためだと思われます(私たちが好まない方法で)。対照的に、execに文字列配列が与えられると、構文解析せずにオペレーティングシステムに渡されます。

実際、忙しい時間を割いてソースコードを見ると( 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);
}
24
Tihamer

ランタイムを作成して、各プロセスを実行します。最初のランタイムからOutputStreamを取得し、2番目のランタイムからInputStreamにコピーします。

6
SJuan76