Java hangs even if the execution of the script is complete

I'm trying to execute a script from my java code, as follows:

Process p = Runtime.getRuntime().exec(cmdarray,envp,dir); // cmdarray is a String array
// consisting details of the script and its arguments

final Thread err = new Thread(...); // Start reading error stream
err.start();
final Thread out = new Thread(...); // Start reading output stream
out.start();
p.waitFor();
// Close resources

The execution of the script is over (its PID no longer exists), but Java still stays on the waitfor () method of the process! Yes, I'm reading the output and error streams in 2 separate threads Yes, they finally joined (after waitfor())

The script basically installs several RPMs (such as about 10) and configures them So the script ran for more than 60 seconds

It looks similar to the following:

#!/bin/sh

#exec 3>&1 >/var/log/some_log 2>&1

# If the above line is uncommented,Java recognizes that the 
# process is over and terminates fine.

tar xzf a-package-having-rpms.tar.gz
cd unpacked-folder
(sh installer-script.sh) #This installs 10 odd rpms in a subshell and configures them
cd ..
rm -rf unpacked-folder

exit 0

Surprisingly, if I put the following line in the script (at the top), Java understands that the script is over and it terminates the process perfectly

exec 3>&1 > /var/log/some_log 2>&1

For records, the script does not generate any output Zero characters! So it makes no sense to put the exec statement there!

But, magically, putting exec statements in scripts makes Java work! Why?

How can I avoid that illogical exec statement in the script?

If you are interested in installer script If you are interested in the content of SH, then:

#!/bin/sh

exec 3>&1 >>/var/log/another-log.log 2>&1
INSDIR=$PWD
RPMSDIR=$INSDIR/RPMS
cd $RPMSDIR
#
rpm -i java-3.7.5-1.x86_64.rpm
rpm -i --force perl-3.7.5-1.x86_64.rpm
rpm -i --nodeps MysqL-libs-5.0.51a-1.vs.i386.rpm
rpm -i --nodeps MysqL-5.0.51a-1.vs.i386.rpm
rpm -i --nodeps MysqL-server-5.0.51a-1.vs.i386.rpm
rpm -i --nodeps perl-DBD-MysqL-3.0007-2.el5.i386.rpm
rpm -i --nodeps perl-XML-Parser-2.34-6.1.2.2.1.i386.rpm
.
.
.

Now, why does Java need the exec command to know that the process is over? How can I avoid that exec?, Especially because the first script does not produce any output

Hold your breath and wait for the answer!

Solution

My guess is that Java won't think the script is over until the pipeline it passes to it through stdin / stdout / stderr is closed by the child process That is, there are no more active reader processes on stdin and no more active writer processes on stdout / stderr

When you read on the pipeline, you will not receive the end of file instruction until no more processes open the pipeline output Therefore, if the process forks and the new process inherits an open file handle, the original process terminates, there is still an open file process, and the reader will still wait

Similar to the pipeline you are writing, you will not receive a "pipe break" signal until the last reader closes the pipeline

This problem usually occurs when your script separates background tasks that inherit stdin / stdout / stderr, such as newly installed services

By using exec, you explicitly break the inheritance chain of these pipes so that the background process does not use them

If on Linux, check whether / proc / * / FD has any new services and whether their stdin / stdout / stderr is the same as the pipeline passed by the java process to the script

Run / etc / init The same situation often occurs when you run D / xxx scripts: they complete normally when you run them from the command line, but they seem to hang when you run them from some monitor

Edit:

You said the installer script contains the following lines:

exec 3>&1 >>/var/log/another-log.log 2>&1

The first item, 3 > & 1, clones stdout to file descriptor 3 (see redirections in man bash) As far as I know, FD 3 has no special significance It then opens / var / log / other - log Log replaces stdout and replaces stderr by cloning stdout See the "redirection" section of the bash man page

This means that the new file descriptor 3 is opened for writing on the pipe originally passed in as stdout Programs that expect to become system service daemons typically close file descriptors 0 (stdin), 1 (stdout), and 2 (stderr), but may not disturb any other file descriptors In addition, now that the shell has opened FD - 3, it passes the open file to any commands it executes, including background commands

Do you know any special reason why the installer opens FD 3? My guess is that if you just remove the term "3 > & 1" from the installer, your problem will be solved This will allow you to completely remove exec from the script

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>