প্রসেসবিল্ডার: মূল থ্রেডটি ব্লক না করে স্টার্টআউট এবং প্রারম্ভিক প্রক্রিয়াগুলির স্টারডার ফরওয়ার্ডিং


94

আমি প্রসেসবিল্ডারটি নীচে জাভাতে একটি প্রক্রিয়া তৈরি করছি:

ProcessBuilder pb = new ProcessBuilder()
        .command("somecommand", "arg1", "arg2")
        .redirectErrorStream(true);
Process p = pb.start();

InputStream stdOut = p.getInputStream();

এখন আমার সমস্যাটি হ'ল: আমি প্রসেসের স্টাডাউট এবং / অথবা স্টাডারের মধ্য দিয়ে যা যা চলছে তা ক্যাপচার করতে এবং এটিকে অবিচ্ছিন্নভাবে পুনর্নির্দেশ করতে চাই System.out। আমি চাই যে প্রক্রিয়াটি এবং এর আউটপুট পুনঃনির্দেশটি পটভূমিতে চালিত হোক। এখনও অবধি, আমি এটি করার একমাত্র উপায়টি হ'ল ম্যানুয়ালি একটি নতুন থ্রেড স্পোন করা যা ক্রমাগত থেকে পড়তে হবে stdOutএবং তারপরে উপযুক্ত write()পদ্ধতিটি কল করবে System.out

new Thread(new Runnable(){
    public void run(){
        byte[] buffer = new byte[8192];
        int len = -1;
        while((len = stdOut.read(buffer)) > 0){
            System.out.write(buffer, 0, len);
        }
    }
}).start();

এই পদ্ধতির কাজ যেমন ধরণের, এটি কিছুটা নোংরা অনুভব করে। এবং তার উপরে, এটি আমাকে পরিচালনা এবং সঠিকভাবে শেষ করতে আরও একটি থ্রেড দেয়। এটি করার আরও ভাল উপায় আছে কি?


4
কলিং থ্রেডকে ব্লক করা যদি কোনও বিকল্প ছিল তবে জাভা org.apache.commons.io.IOUtils.copy(new ProcessBuilder().command(commandLine) .redirectErrorStream(true).start().getInputStream(), System.out);
::

উত্তর:


69

একমাত্র উপায় জাভা 6 অথবা তার আগে সঙ্গে একটি তথাকথিত হয় StreamGobbler(যা আপনি তৈরি করতে শুরু হয়):

StreamGobbler errorGobbler = new StreamGobbler(p.getErrorStream(), "ERROR");

// any output?
StreamGobbler outputGobbler = new StreamGobbler(p.getInputStream(), "OUTPUT");

// start gobblers
outputGobbler.start();
errorGobbler.start();

...

private class StreamGobbler extends Thread {
    InputStream is;
    String type;

    private StreamGobbler(InputStream is, String type) {
        this.is = is;
        this.type = type;
    }

    @Override
    public void run() {
        try {
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null)
                System.out.println(type + "> " + line);
        }
        catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
}

জাভা 7-এর জন্য এভেজেনি ডরোফির উত্তর দেখুন।


4
স্ট্রিমগোবলার কি আউটপুটটির সবকটিই ধরবে? এর আউটপুটের কিছু অনুপস্থিতির কি কোনও সম্ভাবনা আছে? প্রক্রিয়াটি বন্ধ হয়ে যাওয়ার পরে স্ট্রিমগোবলার থ্রেডটি কী নিজেই মারা যাবে?
লর্ডঅফ দ্য পিপস

4
এই মুহুর্ত থেকে, ইনপুট স্ট্রিম শেষ হয়েছে, এটিও শেষ হবে।
asgoth

7
জাভা 6 এবং এর আগেরটির জন্য, মনে হয় এটিই একমাত্র সমাধান। জাভা and এবং
তারপরের জন্য

@ এসগোথ প্রক্রিয়াতে ইনপুট প্রেরণের কোনও উপায় আছে কি? আমার প্রশ্নটি এখানে: stackoverflow.com/questions/28070841/… , কেউ যদি আমাকে সমস্যার সমাধান করতে সহায়তা করে তবে আমি কৃতজ্ঞ থাকব।
ডিপসিধু 1313

145

ব্যবহার করুন ProcessBuilder.inheritIO, এটি বর্তমান জাভা প্রক্রিয়াটির মতোই সাব-প্রসেস স্ট্যান্ডার্ড I / O এর উত্স এবং গন্তব্য সেট করে।

Process p = new ProcessBuilder().inheritIO().command("command1").start();

জাভা 7 যদি কোনও বিকল্প না হয়

public static void main(String[] args) throws Exception {
    Process p = Runtime.getRuntime().exec("cmd /c dir");
    inheritIO(p.getInputStream(), System.out);
    inheritIO(p.getErrorStream(), System.err);

}

private static void inheritIO(final InputStream src, final PrintStream dest) {
    new Thread(new Runnable() {
        public void run() {
            Scanner sc = new Scanner(src);
            while (sc.hasNextLine()) {
                dest.println(sc.nextLine());
            }
        }
    }).start();
}

সাবপ্রসেস শেষ হয়ে গেলে থ্রেডগুলি স্বয়ংক্রিয়ভাবে মারা যাবে, কারণ ইওএফ srcহবে।


4
আমি দেখতে পাচ্ছি যে জাভা 7 স্টাডাউট, স্ট্ডার এবং স্টিডিন পরিচালনা করার জন্য একগুচ্ছ আকর্ষণীয় পদ্ধতি যুক্ত করেছে। বেশ সুন্দর. আমি মনে করি আমি জাভা project প্রজেক্টে এটি করার দরকার পরের বার ব্যবহার করব inheritIO()those redirect*(ProcessBuilder.Redirect)দুর্ভাগ্যক্রমে আমার প্রকল্পটি জাভা 6.
O

আহ, ঠিক আছে আমার 1.6 সংস্করণটি
এভেজেনি ডরোফিভ

scবন্ধ করা দরকার?
হটহোটো

আপনি কি স্ট্যাকওভারফ্লো . com/ প্রশ্নগুলি / ৪৩০৫1640/ … নিয়ে সহায়তা করতে পারেন ?
gstackoverflow

মনে রাখবেন যে এটি এটিকে প্যারেন্ট জেভিএমের ওএস ফাইলডেস্কিটারে সেট করে, সিস্টেম.আউট স্ট্রিমগুলিতে নয়। পিতামাতার কনসোল বা শেল পুনঃনির্দেশে লিখতে এটি ঠিক আছে তবে এটি স্ট্রিম লগিংয়ের জন্য কাজ করবে না। তাদের এখনও একটি পাম্প থ্রেড প্রয়োজন (তবে আপনি কমপক্ষে
স্ট্ডারকে স্টিডিনে

20

জাভা 8 ল্যাম্বদা সহ একটি নমনীয় সমাধান যা আপনাকে এমন একটি সরবরাহ Consumerকরতে দেয় যা আউটপুট প্রক্রিয়া করবে (যেমন লগইন করুন) লাইন দ্বারা লাইন। run()কোনও এক-লাইনার যা কোনও চেক করা ব্যতিক্রম ছুঁড়ে দেওয়া নেই। বিকল্পভাবে বাস্তবায়নের ক্ষেত্রে Runnable, Threadঅন্যান্য উত্তরগুলির পরামর্শ অনুসারে এটি প্রসারিত হতে পারে।

class StreamGobbler implements Runnable {
    private InputStream inputStream;
    private Consumer<String> consumeInputLine;

    public StreamGobbler(InputStream inputStream, Consumer<String> consumeInputLine) {
        this.inputStream = inputStream;
        this.consumeInputLine = consumeInputLine;
    }

    public void run() {
        new BufferedReader(new InputStreamReader(inputStream)).lines().forEach(consumeInputLine);
    }
}

তারপরে আপনি এটির উদাহরণস্বরূপ এটি ব্যবহার করতে পারেন:

public void runProcessWithGobblers() throws IOException, InterruptedException {
    Process p = new ProcessBuilder("...").start();
    Logger logger = LoggerFactory.getLogger(getClass());

    StreamGobbler outputGobbler = new StreamGobbler(p.getInputStream(), System.out::println);
    StreamGobbler errorGobbler = new StreamGobbler(p.getErrorStream(), logger::error);

    new Thread(outputGobbler).start();
    new Thread(errorGobbler).start();
    p.waitFor();
}

এখানে আউটপুট স্ট্রিমটি পুনঃনির্দেশিত হয় System.outএবং ত্রুটি স্ট্রিমটি দ্বারা ত্রুটি স্তরে লগ হয় logger


আপনি কীভাবে এটি ব্যবহার করবেন তা দয়া করে প্রসারিত করতে পারেন?
ক্রিস টার্নার

@ রবার্ট সংশ্লিষ্ট ইনপুট / ত্রুটি প্রবাহ বন্ধ হয়ে গেলে থ্রেডগুলি স্বয়ংক্রিয়ভাবে বন্ধ হয়ে যাবে। forEach()মধ্যে run()পর্যন্ত স্ট্রিম খোলা থাকা অবস্থায় পদ্ধতি অবরোধ পরের লাইনে অপেক্ষা করতে হবে। প্রবাহটি বন্ধ হয়ে গেলে এটি প্রস্থান করবে।
আদম মিশালিক

16

এটি নিম্নলিখিত হিসাবে সহজ:

    File logFile = new File(...);
    ProcessBuilder pb = new ProcessBuilder()
        .command("somecommand", "arg1", "arg2")
    processBuilder.redirectErrorStream(true);
    processBuilder.redirectOutput(logFile);

.redirectErrorStream (সত্য) দ্বারা আপনি ত্রুটি এবং আউটপুট প্রবাহকে মার্জ করার প্রক্রিয়াটি বলছেন এবং তারপরে .redirectOutput (ফাইল) দ্বারা আপনি কোনও ফাইলে মার্জড আউটপুটটিকে পুনর্নির্দেশ করবেন।

হালনাগাদ:

আমি এটি নিম্নলিখিত হিসাবে এটি পরিচালনা করেছিলাম:

public static void main(String[] args) {
    // Async part
    Runnable r = () -> {
        ProcessBuilder pb = new ProcessBuilder().command("...");
        // Merge System.err and System.out
        pb.redirectErrorStream(true);
        // Inherit System.out as redirect output stream
        pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
        try {
            pb.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    };
    new Thread(r, "asyncOut").start();
    // here goes your main part
}

এখন আপনি System.out এ মূল এবং asyncOut থ্রেড উভয় আউটপুট দেখতে সক্ষম


এটি প্রশ্নের উত্তর দেয় না: আমি প্রসেসের স্টাডাউট এবং / অথবা স্টাডারের মধ্য দিয়ে যা যা চলছে তা ক্যাপচার করতে চাই এবং এটিকে অবিচ্ছিন্নভাবে সিস্টেম.আউটে পুনর্নির্দেশ করতে চাই। আমি চাই যে প্রক্রিয়াটি এবং এর আউটপুট পুনঃনির্দেশটি পটভূমিতে চালিত হোক।
আদম মিশালিক

@ অ্যাডামমিচালিক, আপনি ঠিক বলেছেন - আমি প্রথমে টুকরোটি পাইনি। দেখানোর জন্য ধন্যবাদ।
nike.laos

এটি ইনরিটিআইআই () হিসাবে একই সমস্যা রয়েছে, এটি প্যারেন্ট জেভিএমএস এফডি 1 লিখবে কিন্তু কোনও সিস্টেম প্রতিস্থাপন করে না।আউটআউটস্ট্রিমগুলি (লগার অ্যাডাপ্টারের মতো)।
eckes

4

উভয় আউটপুট এবং প্রতিক্রিয়াশীল প্রসেসিং ক্যাপচার সহ সাধারণ জাভা 8 সমাধান CompletableFuture:

static CompletableFuture<String> readOutStream(InputStream is) {
    return CompletableFuture.supplyAsync(() -> {
        try (
                InputStreamReader isr = new InputStreamReader(is);
                BufferedReader br = new BufferedReader(isr);
        ){
            StringBuilder res = new StringBuilder();
            String inputLine;
            while ((inputLine = br.readLine()) != null) {
                res.append(inputLine).append(System.lineSeparator());
            }
            return res.toString();
        } catch (Throwable e) {
            throw new RuntimeException("problem with executing program", e);
        }
    });
}

এবং ব্যবহার:

Process p = Runtime.getRuntime().exec(cmd);
CompletableFuture<String> soutFut = readOutStream(p.getInputStream());
CompletableFuture<String> serrFut = readOutStream(p.getErrorStream());
CompletableFuture<String> resultFut = soutFut.thenCombine(serrFut, (stdout, stderr) -> {
         // print to current stderr the stderr of process and return the stdout
        System.err.println(stderr);
        return stdout;
        });
// get stdout once ready, blocking
String result = resultFut.get();

এই সমাধানটি খুব সোজা এগিয়ে। এটি অপ্রত্যক্ষভাবে দেখায় কীভাবে কোনও লগরে পুনর্নির্দেশ করা যায়। উদাহরণস্বরূপ আমার উত্তরটি একবার দেখুন।
কেওক্রোক 10

3

একটি লাইব্রেরি রয়েছে যা আরও ভাল প্রসেসবিল্ডার সরবরাহ করে, zt-exec। এই গ্রন্থাগারটি আপনি যা যা চেয়েছিলেন ঠিক তাই করতে পারে।

আপনার কোডটি প্রসেসবিল্ডারের পরিবর্তে zt-exec এর সাথে দেখতে কেমন হবে:

নির্ভরতা যুক্ত করুন:

<dependency>
  <groupId>org.zeroturnaround</groupId>
  <artifactId>zt-exec</artifactId>
  <version>1.11</version>
</dependency>

কোড :

new ProcessExecutor()
  .command("somecommand", "arg1", "arg2")
  .redirectOutput(System.out)
  .redirectError(System.err)
  .execute();

গ্রন্থাগারের ডকুমেন্টেশন এখানে: https://github.com/zeroturnaround/zt-exec/


2

আমিও কেবল জাভা use ব্যবহার করতে পারি I আমি @ এভেজেনিডোরোফিভের থ্রেড স্ক্যানার বাস্তবায়ন ব্যবহার করেছি। আমার কোডে, একটি প্রক্রিয়া শেষ হওয়ার পরে, আমাকে তাত্ক্ষণিকভাবে আরও দুটি প্রক্রিয়া চালিত করতে হবে যা প্রত্যেকে পুনঃনির্দেশিত আউটপুটটির তুলনা করে (স্টাডআউট এবং স্টার্ডার আশীর্বাদগুলির সাথে একই হয় তা নিশ্চিত করার জন্য একটি পৃথক-ভিত্তিক ইউনিট পরীক্ষা)।

স্ক্যানার থ্রেডগুলি শীঘ্রই পর্যাপ্তভাবে শেষ হয় না, এমনকি আমি প্রক্রিয়াটি সম্পূর্ণ হওয়ার জন্য অপেক্ষা করি। কোডটি সঠিকভাবে কাজ করার জন্য, প্রক্রিয়া শেষ হওয়ার পরে থ্রেডগুলি যোগ হয়েছে কিনা তা নিশ্চিত করতে হবে।

public static int runRedirect (String[] args, String stdout_redirect_to, String stderr_redirect_to) throws IOException, InterruptedException {
    ProcessBuilder b = new ProcessBuilder().command(args);
    Process p = b.start();
    Thread ot = null;
    PrintStream out = null;
    if (stdout_redirect_to != null) {
        out = new PrintStream(new BufferedOutputStream(new FileOutputStream(stdout_redirect_to)));
        ot = inheritIO(p.getInputStream(), out);
        ot.start();
    }
    Thread et = null;
    PrintStream err = null;
    if (stderr_redirect_to != null) {
        err = new PrintStream(new BufferedOutputStream(new FileOutputStream(stderr_redirect_to)));
        et = inheritIO(p.getErrorStream(), err);
        et.start();
    }
    p.waitFor();    // ensure the process finishes before proceeding
    if (ot != null)
        ot.join();  // ensure the thread finishes before proceeding
    if (et != null)
        et.join();  // ensure the thread finishes before proceeding
    int rc = p.exitValue();
    return rc;
}

private static Thread inheritIO (final InputStream src, final PrintStream dest) {
    return new Thread(new Runnable() {
        public void run() {
            Scanner sc = new Scanner(src);
            while (sc.hasNextLine())
                dest.println(sc.nextLine());
            dest.flush();
        }
    });
}

1

এমএসঞ্জেল উত্তরের সংযোজন হিসাবে আমি নিম্নলিখিত কোড ব্লকটি যুক্ত করতে চাই:

private static CompletableFuture<Boolean> redirectToLogger(final InputStream inputStream, final Consumer<String> logLineConsumer) {
        return CompletableFuture.supplyAsync(() -> {
            try (
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            ) {
                String line = null;
                while((line = bufferedReader.readLine()) != null) {
                    logLineConsumer.accept(line);
                }
                return true;
            } catch (IOException e) {
                return false;
            }
        });
    }

এটি প্রক্রিয়াটির ইনপুট স্ট্রিম (stdout, stderr) কে অন্য কোনও গ্রাহকের কাছে পুনঃনির্দেশ করতে দেয়। এটি সিস্টেম.আউট :: প্রিন্টলন বা স্ট্রিং গ্রহণকারী অন্য কোনও জিনিস হতে পারে।

ব্যবহার:

...
Process process = processBuilder.start()
CompletableFuture<Boolean> stdOutRes = redirectToLogger(process.getInputStream(), System.out::println);
CompletableFuture<Boolean> stdErrRes = redirectToLogger(process.getErrorStream(), System.out::println);
System.out.println(stdOutRes.get());
System.out.println(stdErrRes.get());
System.out.println(process.waitFor());

0
Thread thread = new Thread(() -> {
      new BufferedReader(
          new InputStreamReader(inputStream, 
                                StandardCharsets.UTF_8))
              .lines().forEach(...);
    });
    thread.start();

আপনার কাস্টম কোড এর পরিবর্তে যায় ...


-1
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {

    public static void main(String[] args) throws Exception {
        ProcessBuilder pb = new ProcessBuilder("script.bat");
        pb.redirectErrorStream(true);
        Process p = pb.start();
        BufferedReader logReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
        String logLine = null;
        while ( (logLine = logReader.readLine()) != null) {
           System.out.println("Script output: " + logLine);
        }
    }
}

এই লাইনটি ব্যবহার করে: pb.redirectErrorStream(true);আমরা ইনপুট স্ট্রিম এবং ত্রুটি স্ট্রিম একত্রিত করতে পারি


প্রশ্নটিতে যে কোডটি ইতিমধ্যে ব্যবহার করা হয়েছে ...
লর্ডঅফ দ্য পিপস

-2

ডিফল্টরূপে, তৈরি সাবপ্রসেসের নিজস্ব টার্মিনাল বা কনসোল নেই। এর সমস্ত স্ট্যান্ডার্ড আই / ও (অর্থাত্ স্ট্ডিন, স্টডআউট, স্টডার) ক্রিয়াকলাপগুলি প্যারেন্ট প্রসেসে পুনঃনির্দেশিত হবে, যেখানে getOutputStream (), getInputStream (), এবং getErrorStream () পদ্ধতিগুলি ব্যবহার করে প্রাপ্ত স্ট্রিমগুলির মাধ্যমে তাদের অ্যাক্সেস করা যায়। প্যারেন্ট প্রসেসটি ইনপুট ফিড করতে এবং সাবপ্রসেস থেকে আউটপুট পেতে এই স্ট্রিমগুলি ব্যবহার করে। কিছু নেটিভ প্ল্যাটফর্ম কেবল স্ট্যান্ডার্ড ইনপুট এবং আউটপুট স্ট্রিমগুলির জন্য সীমিত বাফার আকার সরবরাহ করে, তত্ক্ষণাত্ ইনপুট স্ট্রিমটি লিখতে বা সাবপ্রসেসের আউটপুট স্ট্রিমটি পড়তে ব্যর্থতা সাবপ্রসেসকে অবরুদ্ধ করতে বা এমনকি অচলাবস্থার কারণ হতে পারে।

https://www.securecoding.cert.org/confluence/display/java/FIO07-J.+Do+not+let+ বহিরাগত + প্রক্রিয়াগুলি + block+on+IO+buffers

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.