Search code examples
javascriptlinuxshellwaitlsof

Check if file written by process A is ready to be read by process B


I have a Java script that executes a k-shell which calls a program that writes file A. Thereafter, the same Java script parses output file A. No other process read or writes file A.

The problem is that file A is not always ready to be parsed in time, which I assume is because the file system is still finishing its job even after the program & calling shell return. This only occurs when there are a lot of processes running.

lsof does not seem to work on my system. If I open a file with an editor, lsof shows me the editor process, but if I pass the filename to lsof I get: lsof: no file use located: filename

I don't think a fileLock will work, as I would have to unlock it before either returning from the program or the k-shell, by which time the file still might not have been closed completely.

My thought was to write a short script that would rename (mv) the file, and then have the parser parse the renamed file, my understanding being that a file cannot be renamed until it is completely written. I put the mv command in a while loop that checks to see if the stdOut from the mv command has anything in it, theory being that if it is empty, we're good to go.

#! /bin/ksh
# Move a file. The idea is that this script will not return until the mv is
# complete. If mv is successful, mv.out.txt should be empty.

mv $1/camber.out $1/camber.out.copy > mv.out.txt
while [[ -s mv.out.txt ]] ; 
do
    echo "mv did not happen"
    mv $1/camber.out $1/camber.out.rename > mv.out.txt
done
echo "mv should have taken place"

exit

I have yet to see an instance where the mv was held up. Any comments, suggestions, hints, or insults would be greatly appreciated. Thanks in advance to all.


Solution

  • I lack the ability to comment, so an answer this shall be. Regardless, what it sounds like is a problem with asynchronous execution- the java program calls the bash script, which is handled as a separate program by the OS and thus runs concurrently with the java program. For everything to run correctly, you just need to make sure that the bash script is run synchronously- that is, the script must finish before Java continues. I believe this SO answer related to blocking should do what you need. The solution is this:

    ProcessBuilder pb = new ProcessBuilder("myscript.sh");
    Process p = pb.start();     // Start the process.
    p.waitFor();                // Wait for the process to finish.
    System.out.println("Script executed successfully");
    

    That should force the Java program to sleep until the bash script is finished.

    The same logic may need to be applied to the k-shell, as you pointed out in your comment below. You can use the wait command, which takes a job as an optional parameter, to wait on a process (if you do not pass in a specific job, the process will wait for all child processes to finish).