[Java] Kinx Library-Process

6 minute read

Kinx Library-Process

Introduction

The script language Kinxdeliveredby”“ItlookslikeJavaScript,thebrain(contents)isRuby,(stabilityisAC/DC)”). The library is the language. So how to use the library.

This time is Process. I needed to start the child process, so I made a quick decision.

System.exec()

A command execution interface that has been provided as a standard since ancient times. It’s convenient because you just call system() at the C level, but it’s inconvenient if you don’t return until it finishes, or you can’t get standard output. However, since the command is executed via the shell, redirection can be used.

So, this time I made a Process class that can do various things, so that is the favorite in this explanation.

Process

using Process

The Process library is not built-in, so it is explicitly loaded using the using directive.

using Process;

Exec

Create a Process object with new Process(command, opts). The argument is a command name and an array of arguments, or a command string. In the case of an array, it is like passing arguments separately, and in the case of a command line character string, it is analyzed internally and automatically decomposed into an array format.

  • Array: Feels like ["ls", "-1"].
  • String: Feels like "ls -1".

The created process object has the following methods.

Method Overview
run() Starts the process.
launch() Starts the process and disconnects it.
std() Returns the options passed as arguments.
{ in: opts.in, out: opts.out, err: opts.err }

Not done yet at this point. It is started when run() or launch() is executed. run() returns an object of the ProcessController class.

Run

By doing run(), an object of ProcessController class is returned.

var p = new Process(["cmd","arg1"]).run();

Launch

launch() returns nothing (or returns null). The method of not taking care of the child after pushing it away.

new Process(["cmd","arg1"]).launch();

ProcessController

The ProcessController class returned by run() has the following methods.

Method Overview
isAlive() true if the process is alive, false if it has already exited, or false after being detach
wait() Waits for the end of the process, and then returns the exit code of the process. After detach, 0 is returned.
detach() detaches the process

detach() is detached after starting the process. On Linux, the operation is slightly different from when detached with launch(), but the thing I want to do is the same. The internal operation is the same on Windows.

In Linux, it is detached by the so-called double-fork method in order to detach it at process startup, but this can only be used at process startup. It is practically impossible to detach it after the process is started, and the child will survive as a zombie unless it is properly wait or waitpid in the parent process.

So, at the moment of doing detach(), I start a thread just for waitpid and take care of the child until death.

By the way, double-fork is Linux,

  • When the parent process dies, the child process will be under init and init will wait for you

By using the function called …, you can fork a process that was once forked, then quickly terminate the first forked process and let init manage the grandchild process.

The top parent process must remember the child’s waitpid that first forked. My grandson will take care of it for me.

Wait

An example of waiting for the end and acquiring the end code is as follows.

var p = new Process(["cmd", "arg1"]).run();
var status = p.wait();

If you are doing detach, you cannot get it (0 is returned).

Detach

The detach that came out earlier. The process can also be detach (detached). If separated, the edge with the child will be cut. You don’t have to wait and wait for the end. Or rather, you can’t do it even if you want to care.

var p = new Process(["cmd", "arg1"]).run();
p.detach();

Pipe

Wait, pipe. The main purpose of making Process is a pipe. The most desired function is that you can freely connect standard input/output with child processes to a pipe to exchange information.

Specify the pipe with opts of new Process(cmd, opts). The following three types of parameters are available.

Parameter Content
in Specify standard input.
Can be specified as pipe object, character string, $stdin
out Specify standard output.
Can be specified as pipe object, string, $stdout or $stderr
err Specifies the standard error output.
Can be specified as pipe object, string, $stdout or $stderr
  • Pipe object … An object for using a pipe. Details will be described later.
  • Character string … Input source, output destination file as file name.
  • $stdin, $stdout, $stderr … Bind the input source and output destination to the standard input/output of this process.

Pipe object

Create a pipe object with new Pipe(). Returns an array of two objects, [Read, Write]. The pipe object has the following methods.

Normally, specify the Write pipe as the out or err of the child process, and read from the Read pipe.

Read Pipe

Do run() before closing the pipe. Because it is set when you do run().

Method  
peek() Returns 0 if there is no data in the pipe, and a number greater than 0 if there. -1 is an error.
read() Gets all pipe data as a string. If there is no data, it returns an empty string.
close() Closes the pipe.
Write Pipe

Do run() before closing the pipe. Because it is set when you do run().

Method        
————- ———————————– ——————————————-   write(data) Writes data to the pipe. Not all can be written, but the number of written bytes is returned.
close() Closes the pipe.      
sample

The general form is as follows.

using Process;

var [r1, w1] = new Pipe();
var p1 = new Process([ "ls", "-1" ], {out: w1 }).run();
w1.close(); // You can close it as it is no longer used
while (p1.isAlive() || r1.peek() >0) {
    var buf = r1.read();
    if (buf.length() <0) {
        System.println("Error...");
        return -1;
    } else if (buf.length()> 0) {
        System.print(buf);
    } else {
        // System.println("no input...");
    }
}
System.println("");

When using Write Pipe on the parent process side, it looks like this.

using Process;

// stdin read from pipe and output to standard output
[r1, w1] = new Pipe();
var p1 = new Process("cat", {in: r1, out: $stdout }).run();
r1.close(); // You can close it as it is no longer used

// send to stdin of p1
var nwrite = w1.write("Message\n");
w1.close(); // Pipe close, transmission end

p1.wait();

By the way, this way you can control the standard output and standard error output.

new Process("cmd", {out: $stdout, err: $stdout }); // merge standard error output to standard output
new Process("cmd", {out: $stderr, err: $stderr }); // join standard output to standard error output
new Process("cmd", {out: $stderr, err: $stdout }); // swap

Pipeline

Since connecting pipes is a rather troublesome work (in other words, which one…?), I also defined a Process.pipeline that does all at once. Put a callback function at the end and use it as follows.

var r = Process.pipeline(cmd1, cmd2, cmd3, ..., &(i, o, pipeline) => (
    // i ... write pipe to stdin for first command
    // o ... read pipe from stdout of last command
    // pipeline ... pipeline object
    // pipeline.input ....... same as i above
    // pipeline.output ...... same as o above
    // pipeline.peek() ... same as pipeline.output.peek()
    // pipeline.read() ... same as pipeline.output.read()
    // same as pipeline.write() ..... pipeline.input.write()
    // pipeline.isAlive() ... true if any process in the pipeline is alive
    // pipeline.wait() ......waits for all processes in the pipeline to complete,
    // return the exit code as an array

    // The return value of the callback becomes the return value of Process.pipeline() as it is.
    return pipeline.wait();
});

It can be used without calling back.

var pipeline = Process.pipeline(cmd1, cmd2, cmd3, ...);
// pipeline ... pipeline object
// omitted below.

in conclusion

Since the child process relationship is different between Windows and Linux, it is a good point of the script to handle such things in a unified manner. However, the commands themselves are different, so that is not easy to absorb. I’m a Windows user, but I’m using UnxUtilstomakesomeUnixcommandsavailableatthecommandpromptaswell.(Idon’tlikeCygwinbecauseitchangestheenvironment…)

So, next time again.