ইনপুট / আউটপুট স্ট্রিম সহ জাভা প্রক্রিয়া


90

আমার নীচে নীচের কোড উদাহরণ রয়েছে। এর মাধ্যমে আপনি বাশ শেলটিতে একটি কমান্ড প্রবেশ করতে পারেন অর্থাত্‍ echo testফলাফলটি প্রতিধ্বনিত হবে। তবে প্রথম পড়ার পরে। অন্যান্য আউটপুট স্ট্রিম কাজ করে না?

কেন এটি বা আমি কিছু ভুল করছি? আমার শেষ লক্ষ্যটি হ'ল একটি থ্রেডেড নির্ধারিত টাস্ক তৈরি করা যা নিয়মিতভাবে / কমান্ডের জন্য একটি আদেশ কার্যকর করে OutputStreamএবং যাতে InputStreamকাজ করতে হয় এবং কাজ বন্ধ না করে। আমি ত্রুটিটিও অনুভব করছি java.io.IOException: Broken pipeকোন ধারণা?

ধন্যবাদ

String line;
Scanner scan = new Scanner(System.in);

Process process = Runtime.getRuntime ().exec ("/bin/bash");
OutputStream stdin = process.getOutputStream ();
InputStream stderr = process.getErrorStream ();
InputStream stdout = process.getInputStream ();

BufferedReader reader = new BufferedReader (new InputStreamReader(stdout));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));

String input = scan.nextLine();
input += "\n";
writer.write(input);
writer.flush();

input = scan.nextLine();
input += "\n";
writer.write(input);
writer.flush();

while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}

input = scan.nextLine();
input += "\n";
writer.write(input);
writer.close();

while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}

"ব্রোকেন পাইপ" এর অর্থ সম্ভবত শিশু প্রক্রিয়াটি শেষ হয়ে গেছে। অন্যান্য কোডগুলি কী কী তা দেখার জন্য আপনার বাকী কোডটি পুরোপুরি তাকাতে হয়নি।
ভানজা

4
পৃথক থ্রেড ব্যবহার করুন, এটি ঠিক কাজ করবে
Johnydep

উত্তর:


139

প্রথমত, আমি লাইনটি প্রতিস্থাপন করার পরামর্শ দেব

Process process = Runtime.getRuntime ().exec ("/bin/bash");

লাইন দিয়ে

ProcessBuilder builder = new ProcessBuilder("/bin/bash");
builder.redirectErrorStream(true);
Process process = builder.start();

প্রসেসবিল্ডার জাভা 5-এ নতুন এবং বাহ্যিক প্রক্রিয়াগুলি সহজতর করে তোলে। আমার মতে, এর সবচেয়ে উল্লেখযোগ্য উন্নতি Runtime.getRuntime().exec()হ'ল এটি আপনাকে শিশু প্রক্রিয়াটির স্ট্যান্ডার্ড ত্রুটিটিকে তার স্ট্যান্ডার্ড আউটপুটে পুনর্নির্দেশের অনুমতি দেয়। এর অর্থ আপনার কাছে কেবল একটি InputStreamপড়তে হবে। এর আগে, স্ট্যান্ডার্ড আউটপুট বাফার ফাঁকা থাকাকালীন (বাচ্চার প্রক্রিয়াটি স্তব্ধ হয়ে থাকতে পারে) বা তদ্বিপরীতভাবে আপনার দুটি পৃথক থ্রেড থাকতে হবে, একটি থেকে পড়া stdoutএবং একটি থেকে পড়া fromstderr

এরপরে, লুপগুলি (যার মধ্যে আপনার দুটি রয়েছে)

while ((line = reader.readLine ()) != null) {
    System.out.println ("Stdout: " + line);
}

readerপ্রক্রিয়াটির স্ট্যান্ডার্ড আউটপুট থেকে পড়া যখন ফাইল-এর-শেষে ফিরে আসে কেবল তখনই প্রস্থান করুন। bashপ্রক্রিয়াটি যখন প্রস্থান করে তখনই এটি ঘটে । প্রক্রিয়াটি থেকে আর কোনও আউটপুট না উপস্থিত হওয়ার কারণে যদি এটি ঘটে তখন ফাইলের শেষের দিকে ফিরে আসবে না। পরিবর্তে, এটি প্রক্রিয়া থেকে আউটপুট পরবর্তী লাইন জন্য অপেক্ষা করবে এবং এটি এই পরবর্তী লাইন না হওয়া পর্যন্ত ফিরে না।

যেহেতু আপনি এই লুপটিতে পৌঁছানোর আগে প্রক্রিয়াটিতে দুটি লাইন ইনপুট প্রেরণ করছেন, সুতরাং এই দুটি লুপের মধ্যে প্রথমটি স্তব্ধ হয়ে যাবে যদি এই দুটি লাইন ইনপুট পরে প্রক্রিয়াটি বের না হয়। এটি অন্য লাইন পড়ার অপেক্ষা করে সেখানে বসে থাকবে, তবে এটি পড়ার জন্য আর কোনও লাইন আর থাকবে না।

আমি আপনার উত্স কোডটি সংকলন করেছি (এই মুহুর্তে আমি উইন্ডোজটিতে আছি, তাই আমি এর সাথে প্রতিস্থাপন /bin/bashকরেছি cmd.exe, তবে নীতিগুলি একই হওয়া উচিত) এবং আমি পেয়েছি যে:

  • দুটি লাইনে টাইপ করার পরে, প্রথম দুটি কমান্ডের আউটপুট উপস্থিত হয়, কিন্তু তারপরে প্রোগ্রামটি স্তব্ধ হয়ে যায়,
  • যদি আমি টাইপ করি, বলি, echo testএবং তারপরে exit, cmd.exeপ্রক্রিয়াটি প্রস্থান হওয়ার পরে প্রোগ্রামটি এটি প্রথম লুপ থেকে বের করে দেয়। প্রোগ্রামটি তখন ইনপুটটির অন্য একটি লাইন জিজ্ঞাসা করে (যা উপেক্ষা করা হয়), শিশু প্রক্রিয়া ইতিমধ্যে উপস্থিত হয়ে যাওয়ার পরে সরাসরি দ্বিতীয় লুপটি এড়িয়ে যায় এবং তারপরে নিজেই উপস্থিত হয়।
  • যদি আমি টাইপ করি exitএবং তারপরে echo testপাইপ বন্ধ হওয়ার অভিযোগ পেয়ে আমি একটি আইওএক্সেপশন পেয়ে যাই। এটি আশা করা যায় - ইনপুটটির প্রথম লাইনটি প্রক্রিয়াটি প্রস্থান করেছিল এবং দ্বিতীয় লাইনটি প্রেরণের কোথাও নেই।

আমি এমন একটি কৌশল দেখেছি যা আপনি যা চান বলে মনে হয় তার অনুরূপ কিছু করে, একটি প্রোগ্রামে আমি কাজ করতাম। এই প্রোগ্রামটি বেশ কয়েকটি শেল রাখে, সেগুলিতে কমান্ড চালায় এবং এই কমান্ডগুলি থেকে আউটপুট পড়ে। ব্যবহৃত কৌশলটি সর্বদা একটি 'ম্যাজিক' লাইন লিখতে ছিল যা শেল কমান্ডের আউটপুটটির শেষ চিহ্নিত করে এবং শেলকে পাঠানো কমান্ড থেকে আউটপুট কখন শেষ হয়েছিল তা নির্ধারণ করতে এটি ব্যবহার করে।

আমি আপনার কোডটি নিয়েছি এবং writerনীচের লুপটির সাথে নির্ধারিত রেখার পরে আমি সমস্ত কিছু প্রতিস্থাপন করেছি :

while (scan.hasNext()) {
    String input = scan.nextLine();
    if (input.trim().equals("exit")) {
        // Putting 'exit' amongst the echo --EOF--s below doesn't work.
        writer.write("exit\n");
    } else {
        writer.write("((" + input + ") && echo --EOF--) || echo --EOF--\n");
    }
    writer.flush();

    line = reader.readLine();
    while (line != null && ! line.trim().equals("--EOF--")) {
        System.out.println ("Stdout: " + line);
        line = reader.readLine();
    }
    if (line == null) {
        break;
    }
}

এটি করার পরে, আমি নির্ভরযোগ্যভাবে কয়েকটি কমান্ড চালাতে পারি এবং প্রতিটি থেকে আউটপুট স্বতন্ত্রভাবে আমার কাছে ফিরে আসতে পারে।

echo --EOF--শেলের কাছে প্রেরিত লাইনে দুটি কমান্ড রয়েছে যাতে কমান্ড --EOF--থেকে ত্রুটির ফলেও কমান্ড থেকে আউটপুট সমাপ্ত হয় তা নিশ্চিত করা যায় ।

অবশ্যই, এই পদ্ধতির সীমাবদ্ধতা রয়েছে। এই সীমাবদ্ধতা অন্তর্ভুক্ত:

  • যদি আমি কোনও কমান্ড প্রবেশ করি যা ব্যবহারকারীর ইনপুট (যেমন অন্য একটি শেল) এর জন্য অপেক্ষা করে, প্রোগ্রামটি হ্যাঙ্গ হয়ে যায়,
  • এটি ধরে নেওয়া হয় যে শেল দ্বারা চালিত প্রতিটি প্রক্রিয়া একটি নতুন লাইন দিয়ে তার আউটপুট শেষ করে,
  • শেল দ্বারা চালিত কমান্ডটি একটি লাইন লেখার জন্য ঘটলে এটি কিছুটা বিভ্রান্ত হয়ে পড়ে --EOF--
  • bashএকটি সিনট্যাক্স ত্রুটির প্রতিবেদন করে এবং যদি আপনি কোনও মিল না দিয়ে কিছু পাঠ্য প্রবেশ করেন তবে প্রস্থান করে )

আপনি যদি নির্ধারিত টাস্ক হিসাবে চালানোর কথা ভাবছেন তবে তা কোনও কমান্ড বা একটি ছোট্ট কমান্ডের মধ্যে সীমাবদ্ধ থাকবে যা কখনই এ জাতীয় রোগতাত্ত্বিক পদ্ধতিতে আচরণ করবে না যদি এই পয়েন্টগুলি আপনার কাছে কিছু না আসে।

সম্পাদনা : লিনাক্স এ চালানোর পরে প্রস্থান হ্যান্ডলিং এবং অন্যান্য ছোটখাটো পরিবর্তনগুলি উন্নতি করুন।


বিস্তৃত উত্তরের জন্য আপনাকে ধন্যবাদ তবে আমি মনে করি আমি আমার সমস্যার জন্য সত্যিকারের কেসটি চিহ্নিত করেছি। আপনার পোস্টে আমার নজরে আনা। stackoverflow.com/questions/3645889/… । ধন্যবাদ.
জেমস মুর

4
সেন্টিমিডির সাথে / বিন / বাশ প্রতিস্থাপন করা সত্যই একই রকম আচরণ করে না, যেমন আমি প্রোগ্রাম তৈরি করেছি তবে ব্যাশ নিয়ে সমস্যা ছিল। মাইকেসে, প্রতিটি ইনপুট / আউটপুট / এররের জন্য তিনটি পৃথক থ্রেড খোলা দীর্ঘ অধিবেশন ইন্টারেক্টিভ কমান্ডগুলির জন্য কোনও সমস্যা ছাড়াই সেরা কাজ করে।
জনডিপ


@ অ্যালেক্সমিলস: আপনার দ্বিতীয় মন্তব্যটির অর্থ কী আপনি নিজের সমস্যার সমাধান করেছেন? সমস্যাটি কী তা বলার জন্য আপনার প্রথম মন্তব্যে পর্যাপ্ত তথ্যের কাছাকাছি কোথাও নেই বা কেন আপনি 'হঠাৎ' ব্যতিক্রম পাচ্ছেন।
লুক উডওয়ার্ড

@ লুক, আপনার মন্তব্যের ঠিক উপরে দেওয়া লিঙ্কটি আমার সমস্যার সমাধান করে
আলেকজান্ডার মিলস

4

আমি মনে করি আপনি আপনার ইনপুট পড়ার জন্য রাক্ষস-সুতোর মতো থ্রেড ব্যবহার করতে পারেন এবং আপনার আউটপুট রিডারটি ইতিমধ্যে মূল থ্রেডের মধ্যে থাকবে যাতে আপনি একই সাথে পড়তে এবং লিখতে পারেন You আপনি আপনার প্রোগ্রামটি এভাবে পরিবর্তন করতে পারেন:

Thread T=new Thread(new Runnable() {

    @Override
    public void run() {
        while(true)
        {
            String input = scan.nextLine();
            input += "\n";
            try {
                writer.write(input);
                writer.flush();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

    }
} );
T.start();

এবং আপনি পাঠক উপরের মত একই হতে পারে

while ((line = reader.readLine ()) != null) {
    System.out.println ("Stdout: " + line);
}

আপনার লেখককে চূড়ান্ত হিসাবে তৈরি করুন অন্যথায় এটি অভ্যন্তরীণ শ্রেণীর দ্বারা অ্যাক্সেস করতে সক্ষম হবে না।


1

আপনার কাছে writer.close();আপনার কোডে। সুতরাং বাশ তার উপর EOF গ্রহণ করে stdinএবং প্রস্থান করে। তারপরে আপনি অকার্যকর বাশটি Broken pipeথেকে পড়ার চেষ্টা করার সময় পান stdout

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