Sending Command to Java -Jar Using Stdin via /Proc/{Pid}/Fd/0

Is There Any Way to Pipe Data from Perl to a Unix Command Line Utility

If I am reading your question correctly, you are wanting to spawn a process and be able to both write to its stdin and read from its stdout. If that is the case, then IPC::Open2 is exactly what you need. (Also see IPC::Open3 you also need to read from the process' stderr.)

Here is some sample code. I have marked the areas you will have to change.

#!/usr/bin/perl

use strict;
use warnings;

use IPC::Open2;

# Sample data -- ignore this.
my @words = qw(the quick brown fox jumped over the lazy dog);

# Automatically reap child processes. This is important when forking.
$SIG{'CHLD'} = 'IGNORE';

# Spawn the external process here. Change this to the process you need.
open2(*READER, *WRITER, "wc -c") or die "wc -c: $!";

# Fork into a child process. The child process will write the data, while the
# parent process reads data back from the process. We need to fork in case
# the process' output buffer fills up and it hangs waiting for someone to read
# its output. This could cause a deadlock.
my $pid;
defined($pid = fork()) or die "fork: $!";

if (!$pid) {
# This is the child.

# Close handle to process' stdout; the child doesn't need it.
close READER;

# Write out some data. Change this to print out your data.
print WRITER $words[rand(@words)], " " for (1..100000);

# Then close the handle to the process' stdin.
close WRITER;
# Terminate the child.
exit;
}

# Parent closes its handle to the process' stdin immediately! As long as one
# process has an open handle, the program on the receiving end of the data will
# never see EOF and may continue waiting.
close WRITER;

# Read in data from the process. Change this to whatever you need to do to
# process the incoming data.
print "READ: $_" while (<READER>);

# Close the handle to the process' stdin. After this call, the process should
# be finished executing and will terminate on its own.
close READER;

Attach to a processes output for viewing

There are a few options here. One is to redirect the output of the command to a file, and then use 'tail' to view new lines that are added to that file in real time.

Another option is to launch your program inside of 'screen', which is a sort-of text-based Terminal application. Screen sessions can be attached and detached, but are nominally meant only to be used by the same user, so if you want to share them between users, it's a big pain in the ass.

What are file descriptors, explained in simple terms?

In simple words, when you open a file, the operating system creates an entry to represent that file and store the information about that opened file. So if there are 100 files opened in your OS then there will be 100 entries in OS (somewhere in kernel). These entries are represented by integers like (...100, 101, 102....). This entry number is the file descriptor.
So it is just an integer number that uniquely represents an opened file for the process.
If your process opens 10 files then your Process table will have 10 entries for file descriptors.

Similarly, when you open a network socket, it is also represented by an integer and it is called Socket Descriptor.
I hope you understand.

Confused about stdin, stdout and stderr?

Standard input - this is the file handle that your process reads to get information from you.

Standard output - your process writes conventional output to this file handle.

Standard error - your process writes diagnostic output to this file handle.

That's about as dumbed-down as I can make it :-)

Of course, that's mostly by convention. There's nothing stopping you from writing your diagnostic information to standard output if you wish. You can even close the three file handles totally and open your own files for I/O.

When your process starts, it should already have these handles open and it can just read from and/or write to them.

By default, they're probably connected to your terminal device (e.g., /dev/tty) but shells will allow you to set up connections between these handles and specific files and/or devices (or even pipelines to other processes) before your process starts (some of the manipulations possible are rather clever).

An example being:

my_prog <inputfile 2>errorfile | grep XYZ

which will:

  • create a process for my_prog.
  • open inputfile as your standard input (file handle 0).
  • open errorfile as your standard error (file handle 2).
  • create another process for grep.
  • attach the standard output of my_prog to the standard input of grep.

Re your comment:

When I open these files in /dev folder, how come I never get to see the output of a process running?

It's because they're not normal files. While UNIX presents everything as a file in a file system somewhere, that doesn't make it so at the lowest levels. Most files in the /dev hierarchy are either character or block devices, effectively a device driver. They don't have a size but they do have a major and minor device number.

When you open them, you're connected to the device driver rather than a physical file, and the device driver is smart enough to know that separate processes should be handled separately.

The same is true for the Linux /proc filesystem. Those aren't real files, just tightly controlled gateways to kernel information.

Get OutputStream from already running process

This looks possible on Linux, no idea about elsewhere. Searching for "get stdin of running process" revealed several promising looking discussions:

  • Writing to stdin of background process
  • Write to stdin of a running process using pipe
  • Can I send some text to the STDIN of an active process running in a screen session?

Essentially, you can write to the 0th file descriptor of a process via /proc/$pid/fd/0. From there, you just have to open an OutputStream to that path.

I just tested this (not the Java part, that's presumably straightforward) and it worked as advertized:

Shell-1 $ cat

This blocks, waiting on stdin

Shell-2 $ ps aux | grep 'cat$' | awk '{ print $2 }'
1234
Shell-2 $ echo "Hello World" > /proc/1234/fd/0

Now back in Shell-1:

Shell-1 $ cat
Hello World

Note this does not close the process's stdin. You can keep writing to the file descriptor.



Related Topics



Leave a reply



Submit