Borular Runtime.exec () ile nasıl çalıştırılır?


104

Aşağıdaki kodu göz önünde bulundurun:

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

Programın çıktısı:

/etc:
adduser.co

Kabuktan koştuğumda tabii ki beklendiği gibi çalışıyor:

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

İnternetler bana boru davranışının platformlar arası olmadığı için Java üreten Java fabrikasında çalışan zeki beyinlerin boruların çalışacağını garanti edemeyeceğini söylüyor.

Bunu nasıl yapabilirim?

Tüm ayrıştırmamı Java yapıları yerine Java yapılarını kullanarak yapmayacağım grepve sedçünkü eğer dili değiştirmek istersem, ayrıştırma kodumu o dilde yeniden yazmak zorunda kalacağım, ki bu kesinlikle yapılmaz.

Kabuk komutlarını çağırırken Java'nın borulama ve yeniden yönlendirme yapmasını nasıl sağlayabilirim?


Bunu şu şekilde görüyorum: Yerel Java dizesi işlemeyle yaparsanız, Java uygulamasının tüm Java desteği platformlarına taşınabilirliği garanti edilir. Otoh, bu kabuk komutları ile yaparsanız, bu langauge değiştirmek daha kolay gelen Java, ancak bir POSIX platformunda olduğunuzda sadece çalışacaktır. Çok az kişi uygulamanın çalıştığı platform yerine uygulamanın dilini değiştiriyor. Bu yüzden mantığınızın biraz meraklı olduğunu düşünüyorum.
Janus Troelsen

command | grep fooSizin gibi basit bir şeyin spesifik durumunda, yalnızca commandJava'da yerel olarak filtrelemeyi çalıştırıp yapmak çok daha iyidir . Kodunuzu biraz daha karmaşık hale getirir, ancak aynı zamanda genel kaynak tüketimini ve saldırı yüzeyini önemli ölçüde azaltırsınız.
üçlü

Yanıtlar:


182

Bir komut dosyası yazın ve ayrı komutlar yerine komut dosyasını yürütün.

Boru, kabuğun bir parçasıdır, bu nedenle şuna benzer bir şey de yapabilirsiniz:

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

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

@Kaj Ne yapmaya seçenekleri eklemek istiyorsa lsyani ls -lrt?
kaustav datta

8
@Kaj Kabuğa bir komut dizisi belirtmek için -c kullanmaya çalıştığınızı görüyorum, ancak bunu neden tek bir dizge yerine bir dizge dizisi yapmanız gerektiğini anlamıyorum?
David Doria

2
Birisi androidburada sürümü arıyorsa , /system/bin/shonun yerine kullanın
Nexen

25

Linux'ta "ps -ef | grep someprocess" dışında benzer bir sorunla karşılaştım.
En azından "ls" ile dilden bağımsız (daha yavaş da olsa) bir Java değişimine sahipsiniz. Örneğin.:

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" ile biraz daha zor çünkü Java'nın bunun için bir API'si yok gibi görünüyor.

Sigar'ın bize yardımcı olabileceğini duydum: https://support.hyperic.com/display/SIGAR/Home

Bununla birlikte, en basit çözüm (Kaj'ın belirttiği gibi) borulu komutu bir dizgi dizisi olarak yürütmektir. İşte tam kod:

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 dizisinin neden boru ile çalıştığına gelince, tek bir dizge değil ... bu evrenin gizemlerinden biridir (özellikle kaynak kodunu okumadıysanız). Bunun, exec tek bir dizge verildiğinde ilk önce onu ayrıştırmasından (bizim sevmediğimiz bir şekilde) olduğundan şüpheleniyorum. Buna karşılık, exec bir dizge dizisi verildiğinde, onu ayrıştırmadan işletim sistemine aktarır.

Aslında yoğun bir günün dışında zaman ayırıp kaynak koduna bakarsak ( http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/ adresinde) Runtime.java # Runtime.exec% 28java.lang.String% 2Cjava.lang.String []% 2Cjava.io.File% 29 ), tam olarak bunun olduğunu görüyoruz:

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

Yönlendirme ile aynı davranışı, yani '>' karakterini görüyorum. String dizilerindeki bu ekstra analiz aydınlatıcı
kris

Yoğun programınızdan bir gün ayırmanıza gerek yok
gözünüzün

6

İşlemlerin her birini çalıştırmak için bir Çalışma Zamanı oluşturun. İlk Runtime'dan OutputStream'i alın ve ikinci Runtime'dan InputStream'e kopyalayın.


1
Java işleminin adres alanına bellek kopyaladığınız ve ardından sonraki işleme geri döndüğünüz için, bunun bir os-native kanaldan çok daha az verimli olduğunu belirtmekte fayda var.
Tim Boudreau

0

@Kaj kabul edilen cevap linux içindir. Bu, Windows için eşdeğer olanıdır:

String[] cmd = {
"cmd",
"/C",
"dir /B | findstr /R /C:"release""
};
Process p = Runtime.getRuntime().exec(cmd);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.