I would really appreciate if you helped me. I'm rewriting a simple Minecraft launcher from Java to Go. Everything is good, in exception of one thing.
I have a start function which executes using os.Exec this command:
java -Xincgc -Xmx1024M -Djava.library.path="/minecraft/bin/natives/" -cp "/minecraft/bin/*" -Dfml.ignoreInvalidMinecraftCertificates=true -Dfml.ignorePatchDiscrepancies=true net.minecraft.launchwrapper.Launch --username user --session session --version 1.6.4 --gameDir "/minecraft" --assetsDir "/minecraft" --tweakClass cpw.mods.fml.common.launcher.FMLTweaker
Everything is fine running this through bash or cmd, but when executed with Go function it returns the following:
Could not find or load main class net.minecraft.launchwrapper.Launch
I think os.exec(Command) cannot properly interpet this part of command:
-cp "/minecraft/bin/*"
Maybe that's because I quoted string "/minecraft/bin/*" with strconv.Quote() function or because of asterisk. I really don't know what's heppening. Ah, btw, the command os.exec has run is correct, though (I read it in stdout with fmt for debugging purposes).
program:
func start(login string, session string, ram string) {
//start game
app := "java"
arg0 := "-Xincgc"
arg1 := "-Xmx" + ram + "M"
arg2 := "-Djava.library.path=" + strconv.Quote(filepath.FromSlash(client+"bin/natives/"))
arg3 := "-cp"
arg4 := strconv.Quote(filepath.FromSlash(client + "bin/*"))
arg5 := "-Dfml.ignoreInvalidMinecraftCertificates=true"
arg6 := "-Dfml.ignorePatchDiscrepancies=true"
arg7 := "net.minecraft.launchwrapper.Launch"
arg8 := "--username"
//arg9 is login
arg10 := "--session"
//arg11 is session
arg12 := "--version 1.6.4"
arg13 := "--gameDir"
arg14 := strconv.Quote(filepath.FromSlash(client))
arg15 := "--assetsDir"
arg16 := strconv.Quote(filepath.FromSlash(client + "assets"))
arg17 := "--tweakClass cpw.mods.fml.common.launcher.FMLTweaker"
cmd := exec.Command(app, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, login, arg10, session, arg12, arg13, arg14, arg15, arg16, arg17)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
fmt.Println(app, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, login, arg10, session, arg12, arg13, arg14, arg15, arg16, arg17)
}
The question "Properly pass arguments to Go Exec" mentions:
exec.Command(...) adds double quotes to the parameters if there is spaces in them, so you only need to escape \" where you need them.
In your case, -cp "/minecraft/bin/*" would be passed to exec.Command as two separate parameters.
If you need quotes within one parameter, you could use a string literal to keep them (as commented in "How do you add spaces to exec.command in golang").
But if, in your case, you need the cp (classpath) to be expended by the shell (as mentioned in "double quotes escaping in golang exec"), then remove the quotes:
exec.Command(..., "-cp", `/minecraft/bin/*`, ...)
Related
Is there a way to bypass the 32-bit java version (maybe a different way to start a proccess in VBA to invoke the 64-bit version cmd, turn off the UAC or some other sort of tweek) that is being "forced" by the following VBA code (this is just an assumption, I am explaining the debugging process below):
handleDbl = Shell("javaw -cp theJar.jar com.java.SampleClass", vbNormalFocus)
The main point here is that I want to share my macro and avoid putting extra instructions on the recipient so I am trying to do everything on the code (I am using late binding on VBA code to avoid to setting References manually and that kind of stuff).
Debugging Process
An error was thrown so I used the following line instead:
handleDbl = Shell("cmd /k java -cp theJar.jar com.java.SampleClass", vbNormalFocus)
And got the error Exception in thread "main" java.lang.UnsupportedClassVersionError: Unsupported major.minor version so I checked the java -version and tried to find out which java was running with:
C:\>where java
C:\Windows\System32\java.exe
C:\Program Files\Java\_anyJava.x.x.x_\bin\java.exe
I went to System32 folder and there was no java there but I knew that redirection happens from there to C:\Windows\SysWOW64 so I compared the previously extracted java version against C:\Windows\SysWOW64\java.exe -version and they matched.
After that I checked my Outlook version and turned out to be a 32-bit installation. That was a hint but it was mostly that and that big *32 next to the cmd.exe in the Task Manager. I don't know if a 64-bit Outlook would make a difference or it would be the same because of the VBA implementation but that's how I concluded the Shell function from VBA is causing 32-bit java call.
Normally there is a JAVA_HOME environment variable set. If so, then you can do something like this:
Dim JavaExe As String
JavaExe = """" & Environ("JAVA_HOME") & "\bin\java.exe"""
handleDbl = Shell("cmd /k " & JavaExe & " -cp theJar.jar com.java.SampleClass", vbNormalFocus)
If it isn't set, you'll have to find it by some searching, before compiling the command.
Sam's answer is great but I just felt uneasy about user going through more settings so I wrote some functions to check java's versions and notify the user if it is not there (would have to install java in that case anyway) so here is my code. It might contain some helpful stuff.
Private Function IsJavaAvailable(ByVal displayMessage As Boolean, Optional ByVal isJavaMandatory As Boolean) As Boolean
Dim availability As Boolean
Dim minJavaVersion As Integer
minJavaVersion = 8
'isJavaSetup is a global variable
If (Not isJavaSetup) Then
javawPathQuoted = GetMinimumJavaVersion(minJavaVersion)
If StrComp(javawPathQuoted, "") <> 0 Then
isJavaSetup = True
End If
SetGlobalVars
End If
If javawPathQuoted = Empty Then
availability = False
Else
availability = True
End If
If (displayMessage) Then
If (isJavaMandatory) Then
If Not availability Then
MsgBox "This functionality is NOT available without Java " & minJavaVersion & "." & _
vbNewLine & vbNewLine & _
"Please install Java " & minJavaVersion & " or higher.", vbCritical, _
"Mimimum Version Required: Java " & minJavaVersion
End If
Else
If Not availability Then
MsgBox "Some features of this functionality were disabled." & _
vbNewLine & vbNewLine & _
"Please install Java " & minJavaVersion & " or higher.", vbExclamation, _
"Mimimum Version Required: Java " & minJavaVersion
End If
End If
End If
IsJavaAvailable = availability
End Function
Private Function GetMinimumJavaVersion(ByVal javaMinimumMajorVersionInt As Integer) As String
'Run a shell command, returning the output as a string
Dim commandStr As String
Dim javawPathVar As Variant
Dim javaPathStr As Variant
Dim javaVersionStr As String
Dim javaMajorVersionInt As Integer
Dim detectedJavaPaths As Collection
Dim javaVersionElements As Collection
Dim javaVersionOutput As Collection
Dim detectedJavaVersions As Collection
Dim suitableJavawPath As String
'Check available javaw executables in the SO
commandStr = "where javaw"
Set detectedJavaPaths = GetCommandOutput(commandStr)
Set detectedJavaVersions = New Collection
For Each javawPathVar In detectedJavaPaths
'Look for java.exe instead of javaw.exe by substituting it in path
' javaw.exe does NOT return version output like java.exe
javaPathStr = StrReverse(Replace(StrReverse(javawPathVar), StrReverse("javaw.exe"), StrReverse("java.exe"), , 1))
commandStr = """" & javaPathStr & """" & " -version"
Set javaVersionOutput = GetCommandOutput(commandStr)
javaVersionStr = javaVersionOutput.item(1)
Debug.Print "Getting java version: ", commandStr
Debug.Print "Version detected: "; javaVersionStr
Set javaVersionElements = SplitOnDelimiter(javaVersionStr, " ")
'Check that output is not an error or something else
'java version "1.8.0_75"
If javaVersionElements.Count > 2 Then
If StrComp(javaVersionElements.item(1), "java", vbTextCompare) = 0 Then
If StrComp(javaVersionElements.item(2), "version", vbTextCompare) = 0 Then
detectedJavaVersions.Add javaVersionStr
'Remove quotes from "1.8.0_75", split on '.', get 2nd item (java major version) and cast it to Integer
javaMajorVersionInt = CInt(SplitOnDelimiter(SplitOnDelimiter(javaVersionElements.item(3), """").item(1), ".").item(2))
'JAR will only run in Java 8 or later
If (javaMajorVersionInt >= javaMinimumMajorVersionInt) Then
'Validate that "javaw.exe" exists since the validation was made with "java.exe"
Debug.Print "Verifying if javaw.exe exists: ", javawPathVar
If Len(Dir(javawPathVar)) > 0 Then
suitableJavawPath = javawPathVar
Debug.Print "A suitable javaw.exe version found: ", suitableJavawPath
Exit For
End If
End If
End If
End If
End If
Next javawPathVar
GetMinimumJavaVersion = suitableJavawPath
End Function
Private Function GetCommandOutput(ByRef commandStr As String) As Collection
'Run a shell command, returning the output as a string
Dim shellObj As Object
Set shellObj = CreateObject("WScript.Shell")
'run command
Dim wshScriptExecObj As Object
Dim stdOutObj As Object
Dim stdErrObj As Object
Set wshScriptExecObj = shellObj.Exec(commandStr)
Set stdOutObj = wshScriptExecObj.StdOut
Set stdErrObj = wshScriptExecObj.StdErr
'handle the results as they are written to and read from the StdOut object
Dim fullOutputCollection As Collection
Set fullOutputCollection = New Collection
Dim lineStr As String
While Not stdOutObj.AtEndOfStream
lineStr = stdOutObj.ReadLine
If lineStr <> "" Then
fullOutputCollection.Add lineStr
End If
Wend
If fullOutputCollection.Count = 0 Then
While Not stdErrObj.AtEndOfStream
lineStr = stdErrObj.ReadLine
If lineStr <> "" Then
fullOutputCollection.Add lineStr
End If
Wend
End If
Set GetCommandOutput = fullOutputCollection
End Function
I'm trying to install the most current platform (x64 or x86) appropriate Java Runtime Environment via Inno Setup (along with another application). I've found some script examples for how to detect the version and install if correct and adapted them to my needs but I keep running into this:
Unable to open file "path\to\JREInstall.exe":
CreateProcess failed: Code 5:
Access Is Denied
This is the code strictly responsible for installing the JRE:
[Setup]
AppName="JRE Setup"
AppVersion=0.1
DefaultDirName="JRE Setup"
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Files]
Source: "jre-8u11-windows-x64.exe"; DestDir: "{tmp}\JREInstall.exe"; \
Check: IsWin64 AND InstallJava();
Source: "jre-8u11-windows-i586.exe"; DestDir: "{tmp}\JREInstall.exe"; \
Check: (NOT IsWin64) AND InstallJava();
[Run]
Filename: "{tmp}\JREInstall.exe"; Parameters: "/s"; \
Flags: nowait postinstall runhidden runascurrentuser; Check: InstallJava()
[Code]
procedure DecodeVersion(verstr: String; var verint: array of Integer);
var
i,p: Integer; s: string;
begin
{ initialize array }
verint := [0,0,0,0];
i := 0;
while ((Length(verstr) > 0) and (i < 4)) do
begin
p := pos ('.', verstr);
if p > 0 then
begin
if p = 1 then s:= '0' else s:= Copy (verstr, 1, p - 1);
verint[i] := StrToInt(s);
i := i + 1;
verstr := Copy (verstr, p+1, Length(verstr));
end
else
begin
verint[i] := StrToInt (verstr);
verstr := '';
end;
end;
end;
function CompareVersion (ver1, ver2: String) : Integer;
var
verint1, verint2: array of Integer;
i: integer;
begin
SetArrayLength (verint1, 4);
DecodeVersion (ver1, verint1);
SetArrayLength (verint2, 4);
DecodeVersion (ver2, verint2);
Result := 0; i := 0;
while ((Result = 0) and ( i < 4 )) do
begin
if verint1[i] > verint2[i] then
Result := 1
else
if verint1[i] < verint2[i] then
Result := -1
else
Result := 0;
i := i + 1;
end;
end;
function InstallJava() : Boolean;
var
ErrCode: Integer;
JVer: String;
InstallJ: Boolean;
begin
RegQueryStringValue(
HKLM, 'SOFTWARE\JavaSoft\Java Runtime Environment', 'CurrentVersion', JVer);
InstallJ := true;
if Length( JVer ) > 0 then
begin
if CompareVersion(JVer, '1.8') >= 0 then
begin
InstallJ := false;
end;
end;
Result := InstallJ;
end;
In the full setup script the same message continues to come up.
How can I get the JRE Setup to run from this scripted setup file?
I was able to figure out the issue:
Evidently I was mistaken in my use of these lines:
Source: "jre-8u11-windows-x64.exe"; DestDir: "{tmp}\JREInstall.exe"; Check: IsWin64 AND InstallJava();
Source: "jre-8u11-windows-i586.exe"; DestDir: "{tmp}\JREInstall.exe"; Check: (NOT IsWin64) AND InstallJava();
and they should have been in place like so:
Source: "jre-8u11-windows-x64.exe"; DestDir: "{tmp}"; DestName: "JREInstall.exe"; Check: IsWin64 AND InstallJava();
Source: "jre-8u11-windows-i586.exe"; DestDir: "{tmp}"; DestName: "JREInstall.exe"; Check: (NOT IsWin64) AND InstallJava();
That seems to have solved the problem.
Also this line I was mistaken in:
Filename: "{tmp}\JREInstall.exe"; Parameters: "/s"; Flags: nowait postinstall runhidden runascurrentuser; Check: InstallJava()
It should have been:
Filename: "{tmp}\JREInstall.exe"; Parameters: "/s"; Flags: nowait runhidden runascurrentuser; Check: InstallJava()
This is the best solution my limited experience with this particular tool is able to come up with. I will look into the PrepareToInstall option when I have a chance but this works for now.
According to the initial question, "How do I install a JRE from an Inno script?", and taking as a starting solution the best proposed one, I propose a solution that I think works more coherently.
I understand that the user wants to install a JRE for their application if the target computer does not have installed a Java runtime environment or its version is lower than the required one. Ok, what I propose is to use the AfterInstall parameter and reorder a bit the distribution files in a different way.
We will first sort the files in the [Files] section in another way, putting first the redist install files.
Source: "redist\jre-8u121-windows-i586.exe"; DestDir: "{tmp}"; DestName: "JREInstaller.exe";\
Flags: deleteafterinstall; AfterInstall: RunJavaInstaller(); \
Check: (NOT IsWin64) AND InstallJava();
Source: "redist\jre-8u121-windows-x64.exe"; DestDir: "{tmp}"; DestName: "JREInstaller.exe"; \
Flags: deleteafterinstall; AfterInstall: RunJavaInstaller(); \
Check: IsWin64 AND InstallJava();
Source: "Myprog.exe"; DestDir: "{app}"; Flags: ignoreversion
The next step we must do is to modify the section [Run] as follows.
[Run]
Filename: "{app}\{#MyAppExeName}"; \
Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; \
Flags: nowait postinstall skipifsilent
And last but not least, we implemented in the [Code] section the RunJavaInstaller() procedure as follows:
[Code]
procedure RunJavaInstaller();
var
StatusText: string;
ResultCode: Integer;
Path, Parameters: string;
begin
Path := '{tmp}\JREInstaller.exe';
{ http://docs.oracle.com/javase/8/docs/technotes/guides/install/config.html#table_config_file_options }
Parameters := '/s INSTALL_SILENT=Enable REBOOT=Disable SPONSORS=Disable REMOVEOUTOFDATEJRES=1';
StatusText:= WizardForm.StatusLabel.Caption;
WizardForm.StatusLabel.Caption:='Installing the java runtime environment. Wait a moment ...';
WizardForm.ProgressGauge.Style := npbstMarquee;
try
if not Exec(ExpandConstant(Path), Parameters, '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
{ we inform the user we couldn't install the JRE }
MsgBox('Java runtime environment install failed with error ' + IntToStr(ResultCode) +
'. Try installing it manually and try again to install MyProg.', mbError, MB_OK);
end;
finally
WizardForm.StatusLabel.Caption := StatusText;
WizardForm.ProgressGauge.Style := npbstNormal;
end;
end;
You may need to replace the Enabled value with 1 and the Disabled value with 0 if the Java runtime installer is not working properly. I have not experienced any problem doing it this way. Anyways, in the code you have a comment with the Oracle link if you want to take a look.
Finally, since unfortunately we can not receive the installation progress status of the JRE in any way, we show a message and a progress bar so that the user does not have the feeling that the installer has hung.
To do this, we save the state before, execute Exec with the flag ewWaitUntilTerminated, to wait for that installation to finish before continuing with ours, and we restore the previous state once the function execution has finished.
After installing jre1.8.0_121 on SLES11 and RedHat7, the -Dname=value option seems to be ignored by the nashorn engine when run from command line with jjs unless it is prefixed with -J.
Also the "--" argument delimiter is passed to the javascript when shebang is used.
According to online help it should work like this:
$ /usr/java/jre1.8.0_121/bin/jjs -h
jjs [<options>] <files> [-- <arguments>]
-D (-Dname=value. Set a system property. This option can be repeated.)
In the example below I have deliberately left a shebang! header in the javascript. It should not be in there but exposes another difference between u102 and u121 in how the arguments are passed to the args.js javascript.
Tracing the example without -J shows that the local args.log.properties is never read. Instead the default /usr/java/jre1.8.0_121/lib/logging.properties is used. If I replace the /usr/java/jre1.8.0_121/lib/logging.properties with my custom properties file everything is back to normal.
Is this a deliberately changed behavior in u121?
args.sh:
#!/bin/bash -x
/usr/java/jre1.8.0_102/bin/jjs -scripting -Djava.util.logging.config.file=./args.log.properties ./args.js -- arg1 arg2 $*
/usr/java/jre1.8.0_121/bin/jjs -scripting -Djava.util.logging.config.file=./args.log.properties ./args.js -- arg1 arg2 $*
args.js:
#!/usr/bin/env jjs
print (arguments);
print ($ARG[0]);
var logger = java.util.logging.Logger;
var log = logger.getLogger("args");
log.info("log output");
args.log.properties:
handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler
.level=ALL
java.util.logging.ConsoleHandler.level=ALL
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s %3$s %5$s%6$s%n
When run with 2 different JREs it show the below difference. The custom logging options are ignored and the argument delimiter "--" is passed as first argument to the javascipt. :
$ ./args.sh arg3
+ /usr/java/jre1.8.0_102/bin/jjs -scripting -Djava.util.logging.config.file=./args.log.properties ./args.js -- arg1 arg2 arg3
arg1,arg2,arg3
arg1
2017-02-03 11:34:59 INFO args log output
+ /usr/java/jre1.8.0_121/bin/jjs -scripting -Djava.util.logging.config.file=./args.log.properties ./args.js -- arg1 arg2 arg3
--,arg1,arg2,arg3
--
Feb 03, 2017 11:35:00 AM jdk.nashorn.internal.scripts.Script$args :program
INFO: log output
$
Adding "-J" in front of -D solves the system property issue and works with both JREs. The shebang issue remains:
$ ./args.sh arg3
+ /usr/java/jre1.8.0_102/bin/jjs -scripting -J-Djava.util.logging.config.file=./args.log.properties ./args.js -- arg1 arg2 arg3
arg1,arg2,arg3
arg1
2017-02-03 14:04:03 INFO args log output
+ /usr/java/jre1.8.0_121/bin/jjs -scripting -J-Djava.util.logging.config.file=./args.log.properties ./args.js -- arg1 arg2 arg3
--,arg1,arg2,arg3
--
2017-02-03 14:04:04 INFO args log output
$
I don't know if this was intentional or not. But I can confirm that I ran into the problem with the -- being passed into my scripts (which do have the shebang). For the time being, I added the following to work around that problem:
if ($ARG.length > 0 && $ARG[0] == "--") {
$ARG.shift(); // because sometimes the come through!?!
}
Just in case a later change reverts it. For clarity, my scripts actually start with:
#!/usr/bin/jjs -scripting
I wonder if the change is related to the answer here:
Writing a `jjs` shebanged script that accepts just arguments
I'm using ms windows
First, I'm really novice in programming and linux and so many question is here, I'm sorry
I wanted to comfile java source code on gvim and searched googled.. found a post and found some code "myjava.vim", his configuration and _vimrc code
and error occured
here is myjava.vim
this if config file for java
~/.vim/myjava.vim
set cindent
set smartindent
set ai
syntax on
" indent config
set sw=4 sts=4 ts=8 et
" compile and execution
map <F6> :!java %:r<SPACE>
map <F7> :w<ENTER>:make<ENTER>
" compile config
set makeprg=javac %\
set errorformat=%A%f:%l:\ %m,%-Z%p^,%-C%.%#
" finding compile error
map ,n :cn<ENTER>
map ,p :cp<ENTER>
map ,l :cl<ENTER>
map ,w :cw<ENTER>
" set block and auto annotation
vmap ,c :s/^/\/\//g<ENTER>
vmap ,uc :s/^\/\///g<ENTER>
" TagList config
nnoremap <silent> <F8> :Tlist<CR>
nnoremap <silent> <F9> :w<CR>:TlistUpdate<CR>
let Tlist_Inc_Winwidth=0
let Tlist_Use_Right_Window=1
" ctags config
set tags=~/.javatags
set complete=.,w,b,u,t,i
" abbreviation config
ab sysout System.out.println();<ESC>hi
ab syserr System.out.println();<ESC>hi
ab debug if (log.isDebugEnabled()) {<CR>log.debug();<CR>}<CR><ESC>kkf(a
in line 14 error occured "don't know %\ option"
and here is _vimrc code
I didn't know where do I have to place this code in _vimrc file and just put in the end.
let Tlist_Ctags_Cmd="C:\Programs\ctags554\ctags.exe"
au BufNewFile,BufRead *.java :source ~/.vim/myjava.vim
I chanded ~/.vim/myjava.vim to C:\Program Files (x86)\Vim\vim74\myjava.vim
I don't know this is right
this is my _vimrc file
_vimrc reads myjava.vim automatically when reading *.java files
set nocompatible
source $VIMRUNTIME/vimrc_example.vim
source $VIMRUNTIME/mswin.vim
behave mswin
set diffexpr=MyDiff()
function MyDiff()
let opt = '-a --binary '
if &diffopt =~ 'icase' | let opt = opt . '-i ' | endif
if &diffopt =~ 'iwhite' | let opt = opt . '-b ' | endif
let arg1 = v:fname_in
if arg1 =~ ' ' | let arg1 = '"' . arg1 . '"' | endif
let arg2 = v:fname_new
if arg2 =~ ' ' | let arg2 = '"' . arg2 . '"' | endif
let arg3 = v:fname_out
if arg3 =~ ' ' | let arg3 = '"' . arg3 . '"' | endif
let eq = ''
if $VIMRUNTIME =~ ' '
if &sh =~ '\<cmd'
let cmd = '""' . $VIMRUNTIME . '\diff"'
let eq = '"'
else
let cmd = substitute($VIMRUNTIME, ' ', '" ', '') . '\diff"'
endif
else
let cmd = $VIMRUNTIME . '\diff'
endif
silent execute '!' . cmd . ' ' . opt . arg1 . ' ' . arg2 . ' > ' . arg3 . eq
endfunction
let Tlist_Ctags_Cmd="C:\Programs\ctags554\ctags.exe"
au BufNewFile,BufRead *.java :source C:\Program Files (x86)\Vim\vim74\myjava.vim
I made myjava.vim and put it in vim74 folder and edited _vimrc file in Vim folder
and I opened any java file, Windows said we don't know "%\ option"
Please correct my code error.
Thank you so much for reading.
The error message you are getting is actually an escaping problem. Inside a set in Vim you need to escape spaces:
set makeprg=javac\ %
Is the correct makeprg setup. % is the filename of the current buffer being edited.
Moreover, have a look at your $VIMRUNTIME variable (just do :echo $VIMRUNTIME in Vim to find it). It is likely that on your system that variable amounts to C:\Program Files (x86)\Vim\vim74
This way you can do the autocmd as follows:
au BufNewFile,BufRead *.java :source $VIMRUNTIME/myjava.vim
Vim uses the forward slash even on MS windows (unless it is in a string that is passed to the command line, or shellslash is set).
Finally, #VGR is right it is much faster to get Vim answers on http://vi.stackexchange.com (our Vim part of the website)
If I execute set PATH=%PATH%;C:\\Something\\bin from the command line (cmd.exe) and then execute echo %PATH% I see this string added to the PATH. If I close and open the command line, that new string is not in PATH.
How can I update PATH permanently from the command line for all processes in the future, not just for the current process?
I don't want to do this by going to System Properties → Advanced → Environment variables and update PATH there.
This command must be executed from a Java application (please see my other question).
You can use:
setx PATH "%PATH%;C:\\Something\\bin"
However, setx will truncate the stored string to 1024 bytes, potentially corrupting the PATH.
/M will change the PATH in HKEY_LOCAL_MACHINE instead of HKEY_CURRENT_USER. In other words, a system variable, instead of the user's. For example:
SETX /M PATH "%PATH%;C:\your path with spaces"
You have to keep in mind, the new PATH is not visible in your current cmd.exe.
But if you look in the registry or on a new cmd.exe with "set p" you can see the new value.
The documentation on how to do this can be found on MSDN. The key extract is this:
To programmatically add or modify system environment variables, add them to the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment registry key, then broadcast a WM_SETTINGCHANGE message with lParam set to the string "Environment". This allows applications, such as the shell, to pick up your updates.
Note that your application will need elevated admin rights in order to be able to modify this key.
You indicate in the comments that you would be happy to modify just the per-user environment. Do this by editing the values in HKEY_CURRENT_USER\Environment. As before, make sure that you broadcast a WM_SETTINGCHANGE message.
You should be able to do this from your Java application easily enough using the JNI registry classes.
I caution against using the command
setx PATH "%PATH%;C:\Something\bin"
to modify the PATH variable because of a "feature" of its implementation. On many (most?) installations these days the variable will be lengthy - setx will truncate the stored string to 1024 bytes, potentially corrupting the PATH (see the discussion here).
(I signed up specifically to flag this issue, and so lack the site reputation to directly comment on the answer posted on May 2 '12. My thanks to beresfordt for adding such a comment)
This Python-script[*] does exactly that:
"""
Show/Modify/Append registry env-vars (ie `PATH`) and notify Windows-applications to pickup changes.
First attempts to show/modify HKEY_LOCAL_MACHINE (all users), and
if not accessible due to admin-rights missing, fails-back
to HKEY_CURRENT_USER.
Write and Delete operations do not proceed to user-tree if all-users succeed.
Syntax:
{prog} : Print all env-vars.
{prog} VARNAME : Print value for VARNAME.
{prog} VARNAME VALUE : Set VALUE for VARNAME.
{prog} +VARNAME VALUE : Append VALUE in VARNAME delimeted with ';' (i.e. used for `PATH`).
{prog} -VARNAME : Delete env-var value.
Note that the current command-window will not be affected,
changes would apply only for new command-windows.
"""
import winreg
import os, sys, win32gui, win32con
def reg_key(tree, path, varname):
return '%s\%s:%s' % (tree, path, varname)
def reg_entry(tree, path, varname, value):
return '%s=%s' % (reg_key(tree, path, varname), value)
def query_value(key, varname):
value, type_id = winreg.QueryValueEx(key, varname)
return value
def yield_all_entries(tree, path, key):
i = 0
while True:
try:
n,v,t = winreg.EnumValue(key, i)
yield reg_entry(tree, path, n, v)
i += 1
except OSError:
break ## Expected, this is how iteration ends.
def notify_windows(action, tree, path, varname, value):
win32gui.SendMessage(win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment')
print("---%s %s" % (action, reg_entry(tree, path, varname, value)), file=sys.stderr)
def manage_registry_env_vars(varname=None, value=None):
reg_keys = [
('HKEY_LOCAL_MACHINE', r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'),
('HKEY_CURRENT_USER', r'Environment'),
]
for (tree_name, path) in reg_keys:
tree = eval('winreg.%s'%tree_name)
try:
with winreg.ConnectRegistry(None, tree) as reg:
with winreg.OpenKey(reg, path, 0, winreg.KEY_ALL_ACCESS) as key:
if not varname:
for regent in yield_all_entries(tree_name, path, key):
print(regent)
else:
if not value:
if varname.startswith('-'):
varname = varname[1:]
value = query_value(key, varname)
winreg.DeleteValue(key, varname)
notify_windows("Deleted", tree_name, path, varname, value)
break ## Don't propagate into user-tree.
else:
value = query_value(key, varname)
print(reg_entry(tree_name, path, varname, value))
else:
if varname.startswith('+'):
varname = varname[1:]
value = query_value(key, varname) + ';' + value
winreg.SetValueEx(key, varname, 0, winreg.REG_EXPAND_SZ, value)
notify_windows("Updated", tree_name, path, varname, value)
break ## Don't propagate into user-tree.
except PermissionError as ex:
print("!!!Cannot access %s due to: %s" %
(reg_key(tree_name, path, varname), ex), file=sys.stderr)
except FileNotFoundError as ex:
print("!!!Cannot find %s due to: %s" %
(reg_key(tree_name, path, varname), ex), file=sys.stderr)
if __name__=='__main__':
args = sys.argv
argc = len(args)
if argc > 3:
print(__doc__.format(prog=args[0]), file=sys.stderr)
sys.exit()
manage_registry_env_vars(*args[1:])
Below are some usage examples, assuming it has been saved in a file called setenv.py somewhere in your current path.
Note that in these examples i didn't have admin-rights, so the changes affected only my local user's registry tree:
> REM ## Print all env-vars
> setenv.py
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied
HKEY_CURRENT_USER\Environment:PATH=...
...
> REM ## Query env-var:
> setenv.py PATH C:\foo
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied
!!!Cannot find HKEY_CURRENT_USER\Environment:PATH due to: [WinError 2] The system cannot find the file specified
> REM ## Set env-var:
> setenv.py PATH C:\foo
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied
---Set HKEY_CURRENT_USER\Environment:PATH=C:\foo
> REM ## Append env-var:
> setenv.py +PATH D:\Bar
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied
---Set HKEY_CURRENT_USER\Environment:PATH=C:\foo;D:\Bar
> REM ## Delete env-var:
> setenv.py -PATH
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied
---Deleted HKEY_CURRENT_USER\Environment:PATH
[*] Adapted from: http://code.activestate.com/recipes/416087-persistent-environment-variables-on-windows/
For reference purpose, for anyone searching how to change the path via code, I am quoting a useful post by a Delphi programmer from this web page: http://www.tek-tips.com/viewthread.cfm?qid=686382
TonHu (Programmer) 22 Oct 03 17:57 I found where I read the original
posting, it's here:
http://news.jrsoftware.org/news/innosetup.isx/msg02129....
The excerpt of what you would need is this:
You must specify the string "Environment" in LParam. In Delphi you'd
do it this way:
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, Integer(PChar('Environment')));
It was suggested by Jordan Russell, http://www.jrsoftware.org, the
author of (a.o.) InnoSetup, ("Inno Setup is a free installer for
Windows programs. First introduced in 1997, Inno Setup today rivals
and even surpasses many commercial installers in feature set and
stability.") (I just would like more people to use InnoSetup )
HTH
In a corporate network, where the user has only limited access and uses portable apps, there are these command line tricks:
Query the user env variables: reg query "HKEY_CURRENT_USER\Environment". Use "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" for LOCAL_MACHINE.
Add new user env variable: reg add "HKEY_CURRENT_USER\Environment" /v shared_dir /d "c:\shared" /t REG_SZ. Use REG_EXPAND_SZ for paths containing other %% variables.
Delete existing env variable: reg delete "HKEY_CURRENT_USER\Environment" /v shared_dir.
This script
http://www.autohotkey.com/board/topic/63210-modify-system-path-gui/
includes all the necessary Windows API calls which can be refactored for your needs. It is actually an AutoHotkey GUI to change the System PATH easily. Needs to be run as an Administrator.