std.process
API সারমর্ম (Beans source থেকে npm run coverage দিয়ে বানানো): 4টা type · 1টা constructor · 28টা instance method · 7টা public field।
std.process অন্য program চালায়। এখানে কোনো shell নেই: program-এর নাম আর প্রতিটা argument হুবহু execvp-এ পৌঁছায়, তাই space, quote বা semicolon-ওয়ালা একটা filename নেহাত একটা filename-ই। কিছু escape করার নেই, shell injection নিয়েও ভাবার নেই। source দেখুন এখানে:
stdlib/std/process/process.b।
import std.processprogram-এর নামে যদি একটা NUL বাইট থাকে, কিংবা কোনো argument, environment entry বা working directory-তে একটা NUL থাকে, তাহলে সেটা চলার আগেই invalid kind-এর error দিয়ে আটকে দেওয়া হয়।
class Output
“class Output” সেকশনএকটা program পুরোপুরি চলে শেষ হলে যা রেখে গেল।
pub class Output
pub status: intpub out: Bytespub err: Bytes
pub fn succeeded() -> boolpub fn terminated_by_signal() -> boolpub fn stdout_text() -> stringpub fn stderr_text() -> stringstatusহলো exit code, আর signal মেরে ফেললে সেই signal number-এর negative।outআরerrহলো ধরে রাখা raw বাইট; output সবসময় text না-ও হতে পারে।succeeded()শুধু exit code 0-এর জন্য true। বাকি সব, signal সহ, failure।terminated_by_signal()true হয় যখনstatusnegative।stdout_text()আরstderr_text()বাইটগুলোকে string হিসেবে decode করে, আর বাকি সব Beans string-এর মতো একটা এম্বেড করা NUL-এ গিয়ে থামে।
class Command
“class Command” সেকশনchain করে করে একটা command গড়া হয়, তারপর চালানো হয়। প্রতিটা builder method command-টাই ফেরত দেয়, তাই chain করা যায়, আর একই command একবার লিখে একাধিকবার চালানো যায়।
new Command(program: string)Builder method-গুলো:
pub fn arg(value: string) -> Commandpub fn cwd(path: string) -> Commandpub fn env(name: string, value: string) -> Commandpub fn stdin_bytes(data: Bytes) -> Commandpub fn stdin_text(data: string) -> Commandpub fn capture_limit(bytes: int) -> Commandargএকটা argument যোগ করে। এটা কখনো parse হয় না, ভাগ হয় না, shell দিয়েও যায় না।cwdchild-কে current directory-র বদলে সেই directory-তে চালায়।envএকটা environment variable সেট করে। প্রথম call-টাই child-কে এই process-এর environment inherit করা থেকে সরিয়ে একটা নতুন খালি environment-এ নিয়ে যায়, যেখানে শুধু যা সেট করা হয় তা-ই থাকে। অর্থাৎ একবার কোনো variable-এর নাম বললেই খালি থেকে শুরু হয়, আর শুধু যেগুলোর নাম বলা হয় সেগুলোই যোগ হয়।stdin_bytes/stdin_textঠিক করে child-এর stdin-এ কী লেখা হবে। সেই বাইটগুলো লেখা হয়ে গেলেই তার stdin বন্ধ হয়ে যায়, তাই যে program EOF পর্যন্ত পড়ে সেটা শেষ হতে পারে।capture_limitপ্রতিটা stream-এর কতটা রাখা হবে তার সীমা বাঁধে। default হলো 8 MiB (8388608 বাইট), তাই যে program অনন্তকাল ধরে print করে সেটা memory শেষ করে দিতে পারে না।
চালানো:
pub fn run() -> Result<Output>pub fn start() -> Result<Child>run()পুরো কাজটা এক call-এই করে: spawn করা, stdin খাওয়ানো, দুটো output stream-ই টেনে নেওয়া, wait করা, আর reap করা। দুটো stream একসাথে টেনে নেওয়াটাই সেই ক্লাসিক deadlock-কে অসম্ভব করে দেয় — যেখানে parent stdout-কে EOF পর্যন্ত পড়তে গিয়ে ঝুলে থাকে, আর এদিকে child stderr লিখতে গিয়ে আটকে থাকে।start()spawn করে সাথে সাথেই ফিরে আসে, হাতে একটা জ্যান্তChildতুলে দেয় — যেটাকে দেখা, তার সাথে কথা বলা, আর থামানো যায়।stdin_bytes,stdin_text, আরcapture_limitstart()-এ খাটে না, কারণ এর stream-গুলো ব্যবহারের জন্য খোলা থাকে; বাকি সব খাটে।
run()-এর একটা ভাগাভাগি খেয়াল রাখতে হবে। যে program শুরুই হতে পারল না (পাওয়া যায়নি, executable না, working directory খারাপ) সেটা একটা err। আর যে program চলল, তারপর fail করল সেটা ok, তবে non-zero status নিয়ে। তাই ok হলেও status চেক করতে হবে।
import std.ioimport std.process
fn main() { let out: process.Output = new process.Command("echo").arg("hello").run().expect("run") io.print(out.stdout_text()) // hello io.println("exit {out.status}")}class Stream
“class Stream” সেকশনএকটা child-এর তিনটা pipe-এর একটা: stdin, stdout, বা stderr।
pub class Stream
pub name: string
pub fn write(data: Bytes) -> Result<int>pub fn write_all(data: Bytes) -> Result<int>pub fn write_text(text: string) -> Result<int>pub fn read(max: int) -> Result<Bytes>pub fn read_to_end(limit: int) -> Result<Bytes>pub fn close() -> Result<bool>pub fn is_open() -> boolpub fn poll_handle() -> intnameবলে দেয় এটা কোন stream।writedata-র কিছুটা লেখে আর কতটা গেল জানায়; কম লেখা স্বাভাবিক।write_allকম-কম-লেখাগুলোর উপর loop করে সব পাঠানো শেষ করে, আর stream কিছুই না নিলেresetkind-এ fail করে।write_textএকটা string লেখে।readবড়জোরmaxবাইট পড়ে; খালি result বলতে বোঝায় অন্য প্রান্ত বন্ধ করে দিয়েছে, তাই child-এর stdout-এর বেলায় ওটা আর লিখছে না।read_to_endwriter বন্ধ না করা পর্যন্ত পড়ে, বড়জোরlimitবাইট। এটা chunk-এর একটা list জোড়া না দিয়ে একটাই result buffer বড় করে।closeএই stream বন্ধ করে। child-এর stdin-এর বেলায় এটাই সেই উপায় — যে program EOF পর্যন্ত পড়ে তাকে শেষ হতে বলা। ইতিমধ্যে বন্ধ হওয়া stream-এ read, write বা close করলেclosedkind-এর error।is_openstream খোলা থাকা পর্যন্ত true।poll_handleraw descriptor-টা borrow করে ফেরত দেয়, একটা poller-এ register করার জন্য।
unique class Child
“unique class Child” সেকশনএকটা চলন্ত child process। এটা move-only, আর drop হওয়ার সময় নিজেই পরিষ্কার হয়ে যায়।
pub unique class Child
pub stdin: Streampub stdout: Streampub stderr: Stream
pub fn process_id() -> intpub fn is_finished() -> Result<bool>pub fn wait() -> Result<int>pub fn wait_timeout(ms: int) -> Result<Option<int>>pub fn terminate() -> Result<bool>pub fn kill() -> Result<bool>pub fn send_signal(number: int) -> Result<bool>pub fn stop(grace_ms: int) -> Result<int>stdin,stdout, আরstderrহলো child-এর তিনটা pipe।process_id()হলো OS process id, logging-এর জন্য।is_finished()child বেরিয়ে গেলে true হয়, আর তখনই তাকে reap করে ফেলে, তাই zombie না রেখে একে loop-এ ডাকা নিরাপদ।wait()exit-এর জন্য অপেক্ষা করে status ফেরত দেয়: exit code, আর signal শেষ করলে সেই signal number-এর negative।wait_timeout(ms)বড়জোরmsমিলিসেকেন্ড অপেক্ষা করে;noneবলতে বোঝায় এখনো চলছে, যেটা error না। negativemsহলোinvalidkind।terminate()SIGTERM পাঠায়,kill()SIGKILL পাঠায়, আরsend_signal(number)number দিয়ে যেকোনো signal পাঠায়।stop(grace_ms)SIGTERM পাঠায়, বড়জোরgrace_msঅপেক্ষা করে, child তখনো থাকলে SIGKILL পাঠায়, আর তার status ফেরত দেয়। বেশিরভাগ caller-এর ঠিক এই গড়নটাই দরকার হয়।- child-কে একবার wait করা বা শেষ হয়ে যাওয়ার পর
wait,wait_timeout,terminate,kill,send_signal, বাstopডাকলেclosedkind-এর error।
একটা Child-কে wait না করেই drop করলে সেটা নিজে থেকে থামানো হয়: SIGTERM, 200 ms grace period, তারপর SIGKILL, আর reap-ও করা হয় যাতে ওটা zombie হয়ে না থাকে। child-কে নিজের মতো করে শেষ হতে দিতে চাইলে wait() ডাকতে হবে।
import std.ioimport std.process
fn main() { let child: process.Child = new process.Command("cat").start().expect("start") child.stdin.write_text("hi\n").expect("write") child.stdin.close().expect("close") let text: Bytes = child.stdout.read_to_end(4096).expect("read") io.print(text.to_string()) // hi child.wait().expect("wait")}আরও দেখুন
“আরও দেখুন” সেকশন- std.signal, নিজের program-এ signal receive করা।
- Concurrency guide।