I'm not too familiar with programming for the Windows desktop and I'm trying to figure out how to handle calling .bat files from Java. I'm trying to make desktop Java program using Maven run as a jar file execute a bash script in a Citrix virtualization Environment.
The original initial problems was that UNC paths were causing to the batch file to not be found, but I've modified the paths and now I'm able to find the batch file, but cmd.exe is disabled in the Citrix account I'm using, and I worried that there's nothing I can do.
Here's how it works:
1)
User launches program via program.jar file User checks for updates
2)
If updates are found, a new jar file called program_update.jar is
downloaded, and a batch file called run_update.bat is copied to the
main program directory.
3)
From within the java program, the following command is called:
Runtime.getRuntime().exec("cmd /c pushd " + FileName.installation + " && start run_update.bat && popd");
where FileName.installation is a property that has been previous set indicating the home installation directory.
run_update.bat runs a loop attempting to replace program.jar with program_update.jar and show a success/fail message in the command prompt.
When I run this in the Citrix environment, I get the following message:
'run_update.bat' is not recognized as an internal or external command, operable program, or batch file.
The command prompt has been disabled by your administrator.
At this point, I can click on run_update.bat and it runs the operation successfully.
The automatic update called via program.jar works in a normal windows environment with access to cmd.
My questions
Is my call from java to run cmd /c <commands> simply not going to work when cmd.exe is disabled?
Would renaming run_update.bat to run_update.cmd do any good?
Any alternative techniques? The batch file is necessary AFAIK because the jar file is replacing itself via the mv command.
I solved my own problem with an alternative technique. It turns out running Runtime.getRuntime().exec("cmd /c pushd " + FileName.installation + " && start run_update.bat && popd"); in a Citrix Environment was the problem, becase access to cmd is restricted. I rewrote it to use the the Desktop class:
File exe = new File(FileName.installation + File.separator + "install_update.bat");
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().open(exe);
} else {
Runtime.getRuntime().exec("cmd /c pushd " + FileName.installation + " && start install_update.bat && popd");
}
Related
shell script file directory: /some/location/myShellScript.sh
Properties-Type: shell script (application/x-shellscript)
EDIT
content of shell script:
#!/bin/bash
export LD_LIBRARY_PATH=`pwd`
echo `pwd`
./someExecutable ../input/cfg/test1.ini
The test1.ini is generated one step before in the java code,
it provides settings for some testing, which is done in the background. Then the shell script ends up with the file I need for further processing.
/EDIT
When I am running this shell script on linux terminal in its own directory just with "./myShellScript.sh" it works perfectly fine...
The part my shell script shall be executed:
//Do something before
//Shell scripts creates a file
String cmd = /some/location/myShellScript.sh;
ProcessBuilder pb = new ProcessBuilder(cmd);
Process process = pb.start();
int exitValue = process.waitFor();
System.out.println(exitValue);
//Afterwards I am processing the generated file
When running my java program as an executable .jar file, this process gets not executed and the exitValue is 127, but I don't know why...
I tried many things like:
using the Runtime to exec
adding #!/bin/bash or #!/bin/sh on top of the shell script
adding a "sh" parameter to the process command in form of String[]
In my execution directory, I changed the permission with chmod 755 -R * recursively so every associated library used by the shell script is indeed available (also due to the fact, that I can just execute it on the terminal).
I really tried to find a proper answer on the internet but I wasn't successful.
And no, I cannot just do everything in java, the shell script is mandatory and cannot be replaced in this case.
Thanks in advance for helpful suggestions!
The script you are executing is highly sensitive to its working directory. It uses pwd to set the LD_LIBRARY_PATH and it attempts to execute another program via a relative path to that program, providing a relative path as a command-line argument, as well.
The working directory for an execution of the script has no essential relationship with the directory in which the script resides -- it completely depends on how and in what context the script is launched. For example, you report that the script works as you expect "When I am running this shell script [...] in its own directory." But when you run the script from Java you very likely are not running it with its own directory as the working directory, and that will strongly affect this script's behavior.
One solution would be to hardcode the script's installation path into the script itself, and to express all your paths relative to that:
#!/bin/bash
installation_dir=/path/to/the/script/dir
export LD_LIBRARY_PATH=$installation_dir
"$installation_dir"/someExecutable "$installation_dir"/../input/cfg/test1.ini
It's a bit kludgy to hardcode the path, though. You could further improve it by having the script identify its own directory at runtime:
#!/bin/bash
installation_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
export LD_LIBRARY_PATH=$installation_dir
"$installation_dir"/someExecutable "$installation_dir"/../input/cfg/test1.ini
That's obviously Bash-specific, but you were using bash anyway. Alternatively, if the executable your script launches is also sensitive to its working directory, then perhaps you just want the script to change directory (which will be effective only for the script and processes downstream from it, not for its parent process):
#!/bin/bash
cd "$( dirname "${BASH_SOURCE[0]}" )"
export LD_LIBRARY_PATH=`pwd`
./someExecutable ../input/cfg/test1.ini
The 127 exit status means that a command used in the script is not found.
EDIT
Debug the script, when bash is used, add the line below on the second line:
exec > /tmp/debug.txt 2>&1 ; set -x
After the next attempt, analyze the traces generated into the /tmp/debug.txt file.
OLD INTRO
(the script content was not yet provided)
The Java program which executes the myShellScript.sh script has probably not the same PATH environment variable than the one which is set in your environment when you execute the script manually from a terminal.
I am trying through Java to call a batch file in another folder directory.
String cmd = "cmd /c start /wait " + backupFolder + "\\script_encrypt.bat";
Process p = Runtime.getRuntime().exec(cmd);
p.waitFor();
However, when the batch file runs, it shows me the current directory which the batch is not there, why?.
Are you sure the batch file path and the path called from Java are the same?
According the screenshot from Eclipse and the screenshot from Cmd, they are not matching.
I have done the same, and worked for me.
I am running a batch (ScanProject.bat) file using java by following code
Process p= Runtime.getRuntime().exec("cmd /c start /wait ScanProject.bat "+ BaseProjDir+"\\"+jo.getString("Name")+" "+st.nextToken());
System.out.println("Exit value : "+p.waitFor());
And following is the batch file code :-
%2:
cd %1
ant -f ..\antbuild.xml analyse
exit
Batch file run successfully but problem is command prompt window do not closes automatically and hence Process do not terminated automatically and my program wait for infinite time to complete this process.Please suggest me any technique so that cmd exit after running ant -f ..\antbuild.xml analyse command.
Thanks.
cd /D "Full path of directory" or pushd "Full path of directory" with popd before exit is better to switch the current directory to any directory on any drive (cd and pushd/popd) or even to a network share (just pushd/popd). Run in a command prompt window cd /? and pushd /? for details.
cmd /C starts a new Windows command process with closing the process automatically after last command was executed. Run in a command prompt window cmd /? for details on options of Windows command interpreter.
start is a command to start a new Windows command process or a GUI/hybrid application in a separate process.
So what you do here is starting a new Windows command process which starts a new Windows command process.
Running in a command prompt window start /? outputs the help for this command. start interprets often the first double quoted string as title string for the new command process. This causes often troubles on command lines with at least 1 double quoted string. Therefore usage of start requires often an explicit definition of a title string in double quotes as first argument for start which can be even an empty string, i.e. simply "" as first argument after start.
As it can be read after running exit /? in a command prompt window, this command without /B always exits the current Windows command process immediately. So when ant.exe finished, the command process in which the batch file was processed is definitely terminated.
I'm having no experience on Java development, but in my point of view it should be enough to use the following execution command which does not need a batch file at all.
The Java code line
Process p= Runtime.getRuntime().exec("cmd.exe /C cd /D \"" + jo.getString("Name") + "\" && ant.exe -f ..\\antbuild.xml analyse");
should be enough to
start a new Windows command process,
set the current directory within this command process to the drive and directory specified by jo.getString("Name") which of course must return a directory path with drive letter and using backslashes as directory separators, and on success
execute ant in this directory with the specified parameters
with terminating the Windows command process automatically after console application ant.exe finished if ant.exe is a console application.
I'm not sure if cmd.exe /C is needed at all.
I suggest to test this command first manually from within a command prompt window. Then use it in the Java application if really working and producing the expected result. And finally I would further test if cmd.exe /C is needed at all in Java code.
See Single line with multiple commands using Windows batch file for details about the operator && to run a command after previous command was successful. And see also Why do not all started applications save the wanted information in the text files as expected? for an explanation of console / GUI / hybrid application.
NOTE: There is also Java Runtime method exec(String[] cmdarray, String[] envp, File dir) to execute a command like ant.exe with its parameters -f and ..\antbuild.xml and analyse in the directory defined with third parameter which might be better for this task.
Swap out exit for taskkill, assuming you do not have any other cmd processes running. Not very graceful but it will get the job done.
%2:
cd %1
ant -f ..\antbuild.xml analyse
taskkill /im cmd.exe
I want to create auto-updater of my program.
In java part it looks like
int pid = Kernel32.INSTANCE.GetCurrentProcessId();
String cmd = folder + "update.bat" + " " + currentLoc + " " + updateLoc + " " + Integer.toString(pid);
Runtime.getRuntime().exec(cmd);
And the batch contains
SET "name=GameDrive Logs Viewer.exe"
SET "myname=update.bat"
TASKKILL /pid %3
TASKKILL /pid %3
DEL "%1\%name%"
MOVE "%2\%name%" "%1"
"%1\%name%"
DEL "%2\%myname%"
So, I'm killing current program and delete it.
Then i move new version to old folder, run new version, and delete the bat file.
This bat file is perfectly works when i call it from cmd with sending parameters.
But nothing is happend when i'm trying use it from java program.
As i found, that all Dialog windows creating from current program have the same processID. (I tested it from another bat).
So, my guess is the batch which is called from my java program get the same processID and kill himself.
Am I right? And if yes - can how I do that?
I guess you need launch your update.bat in another cmd instance as follows (add path as necessary). In JAVA use updatecall.bat with next content:
Either with CMD.exe: Start a new CMD shell and (optionally) run a command/executable program.
cmd /C ""update.bat" %*"
or with START: Start a program, command or batch script (opens in a new window.)
start "" "update.bat" %*
If started a batch file then the command processor is run with the /K switch to cmd.exe. This means that the window will tend to remain after the batch has been run. To auto close it, add EXIT command to the end of started batch.
I'm sure there is a smarter solution without any auxiliary batch...
The code in question has worked in more or less the exact same configuration on earlier releases of Windows, however, it is not known to have run on Windows 7 YET! That's what I need to solve now.
Briefly, some C code performs some configuration and security checks before launching a java program, passing some data that would be neigh-into-impossible to do easily in Java. The Java in turn, at the appropriate time launches the same C code which then itself launches a different Java program. The second program launch needs to be completely independent, (think nohup) hence the second launch.
What's happening now is that the C program launches the Java program in the ordinary way, but when the Java tries to launch the C program, it errors out like this:
/cygdrive/c/opt/ST/v3.3/bin/ST.exe: error while loading shared
libraries: ?: cannot open shared object file: No such file or
directory
Because Windows has been such a bear over the years, the C code is written in the posix environment of Cygwin, but all it really does is ordinary C types of things (nothing about it is unique to Cygwin and, indeed, in the past it has been built with Microsoft's development tools, but that environ is not available at present). The Cygwin environ adds a lot of other great benefits, like command-line management of services (cygrunsrv) and a full-on 'nix-like environment (bash, etc). In fact, because Windows has changed how one launches a program from Java so many times, Cygwin helps standardize the Java launch code. Here's an excerpt:
if (ClientOS.indexOf("Windows") != -1)
{
if (ClientOS.equals("Windows 95"))
{
cmd = "command.com /C ";
} else if (ClientOS.equals("Windows 98"))
{
cmd = "command.com /C ";
//cmd = "cmd.exe /C ";
} else if (ClientOS.equals("Windows NT"))
{
cmd = "cmd.exe /C ";
} else if (ClientOS.equals("Windows 2000"))
{
cmd = "cmd.exe /C ";
} else if (ClientOS.equals("Windows XP"))
{
cmd = "cmd.exe /C ";
} else {
cmd = "cmd.exe /C ";
}
if (cygwin)
{
cmd += Shell+" '"+Command+"'";
} else {
cmd += Command;
}
} else {
cmd = Command;
}
(Yes, the if structure could be better optimized.)
In this case, "Shell" equals:
Shell=C:/cygwin/bin/bash -c
And, there's a test program to ensure the above and supporting code works OK - it runs a bit of shell program and ensures it got back what it thought it should. It says:
Checking the ability to run a program using a shell...
Yes, shell programs work fine.
The final contents of cmd look something like this:
cmd.exe /C C:/cygwin/bin/bash -c '/cygdrive/c/opt/ST/v3.3/bin/ST.exe'
WHAT I SUSPECT:
I suspect what's going on is that the Cygwin1.DLL file isn't being found properly. It lives in C:/cygwin/bin/cygwin1.dll
NOTE THAT both the system-level PATH and the Cygwin PATH include the path to the cygwin .dll files. Moving a copy of cygwin1.dll to the bin directory where the target executable lives didn't work either.
Would LD_LIBRARY_PATH lend any help here? If so, any idea how it is to be set?
Other ideas?
Thanks.
C:/cygwin/bin/bash -c '/cygdrive/c/opt/ST/v3.3/bin/ST.exe'
This won't work because your working directory is where you reside when you executed this command. You will have to copy the cygwin dependent dlls to the directory you execute this from. Othwerwise, you will have to place the cygwin bin directory in your system PATH variable, not sure you want to do that, can cause dll hell.
Also, if you're using anything in your profile, you need to add --login parameter to bash:
bash --login -c
Also, print the final command out at the end, before you spawn the process:
printf('%s\n',cmd)
Just to be sure it's exactly what you want.
You may also run the program with strace, if you're unsure about the dlls being referenced.
Several ways.
cygwin1.dll needs to be in %WINDIR%\system32 or equivalent.
or
you modify the PATH variable to add the path to cygwin1.dll in the caller's environment.
or
you call a .bat file that sets the environment before you call the cygwin built exe.
or
you build a standalone version of the .exe (i.e. no cygwin dependency).