Set environment variables necessary for program, programatically - windows

The project I'm working on requires the GDAL library, with skipping all the stuff, has a bit of complicated installation as they're C-bindings for Java. Anyways I did find a relatively easy precompiled version of it, the only thing is it requires the user to set some environment variables.
That's easy enough to do, but the process can still confuse some people so to keep it simple I want like a simple "setup.bat" that will do that for you in Windows. This is what I got so far:
set "root=%~dp0"
set "gdalpath=%root%gdal-x64-11.1\"
setx PATH "%PATH%;%gdalpath%"
setx GDAL_DATA "%gdalpath%gdal-x64-11.1\gdal-data\"
setx GDAL_DRIVER_PATH "%gdalpath%gdal-x64-11.1\gdalplugins\"
setx PROJ_LIB "%gdalpath%gdal-x64-11.1\projlib\"
It works mostly pretty well except for PATH. There's two PATHs it seems, one for system and one for user environment variables. Simply calling straight %PATH% gives all of them combined, but writing with setx PATH writes it all to the user PATH.
For example if the variables looked like this before:
After running the batch file they look like this:
Which in this example does conserve the "C:\PHP\" and add the GDAL path at the end, but it just looks messy. I mean I think it more or less works and does what it's supposed to, but I'd rather have it more cleanly. So is there a way to grab only the user PATH variables and append to that?

Did you type setx /? for pages of help before posting (like /k switch)? Plus setx doesn't do the current console window.
To test if file is already in the path
#echo off
echo.
echo PathFind - Finds the first file in in a path
echo ======== = ===== === ===== ==== == == = ====
echo.
echo Searching for %1 in %path%
echo.
set a=%~$PATH:1
If "%a%"=="" (Echo %1 not found) else (echo %1 found at %a%)
To read the registry.
#for /f "skip=2 tokens=3" %%A in ('Reg query HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced /v AlwaysShowMenus') do #if "%%A"=="0x1" echo Matches
Pause
User permanent variables are stored here.
HKEY_CURRENT_USER\Environment
VBScript WSH gives far more fine grained access to the environment.
From Help.
The Environment property contains the WshEnvironment object (a collection of environment variables). If strType is supplied, it specifies where the environment variable resides with possible values of System, User, Volatile, or Process.
The following code retrieves the system environment variable NUMBER_OF_PROCESSORS.
Visual Basic Script Copy Code
Set WshShell = WScript.CreateObject("WScript.Shell")
Set WshSysEnv = WshShell.Environment("SYSTEM")
WScript.Echo WshSysEnv("NUMBER_OF_PROCESSORS")

Related

Difference between Dynamic Environment Variables and Normal Environment Variables in CMD

I was reading an article on ss64 about Environment variables in Command Prompt.
Later in the article there exists a table which states environment variables found commonly in Command Prompt. Some of the variables listed there are termed as Volatile (Read Only). A sentence found in the article states:-
Dynamic environment variables are read-only and are computed each time the variable is expanded. When all variables are listed with SET, these will not appear in the list. Do not attempt to directly SET a dynamic variable.
I understood the later two statements. But i can't understand the first one.
Doubts:-
%userprofile% is an non-volatile variable, which resolves to %SystemDrive%\Users\{username}, and %homepath% is a volatile variable which resolves to Users\{Username}. Both commands are quite similar (except for the systemdrive). Then why is one volatile and the other one non-volatile?
What is the criteria for a variable to be dynamic? what makes %appdata% (just an ex.) an non volatile variable?
Dynamic Variables are computed each time the variable is expanded, this makes sense for variables like %CD% %DATE% %TIME% %RANDOM% etc. as they would lose their functionality if they would be non-volatile. But how would it effect %homepath%?
Some non-volatile variables have sort-of dynamic components in them. Ex. %userprofile% has %SystemDrive% and {username} in it's path. Then how are those variables not dynamic?
There are three types of variables of which value is accessed using the syntax %variable% or !variable! on having enabled delayed environment variable expansion with using the option EnableDelayedExpansion of the command setlocal from within a Windows command prompt window or a batch file, i.e. using %SystemRoot%\System32\cmd.exe.
1. Persistent stored variables
There are environment variables stored persistent in Windows registry.
User variables stored in Windows registry under the key:
HKEY_CURRENT_USER\Environment
System variables stored in Windows registry under the key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
The user variables are defined for just the account in which user registry hive they are stored (file %UserProfile%\ntuser.dat). The system variables are defined for all accounts used on a Windows machine (file %SystemRoot%\System32\config\SYSTEM).
The persistent stored variables can be viewed, edited and deleted by opening the Windows Control Panel, clicking on System, clicking next (on left side) on Advanced system settings and clicking on button Environment Variables. The upper half is for the user variables of the current user account and the lower half is for the system variables since Windows XP.
There are defined by default as user variables only TEMP and TMP since Windows XP.
The list of predefined system variables is since Windows XP:
ComSpec
NUMBER_OF_PROCESSORS
OS
PATH
PATHEXT
PROCESSOR_ARCHITECTURE
PROCESSOR_IDENTIFIER
PROCESSOR_LEVEL
PROCESSOR_REVISION
TEMP
TMP
windir
There is on Windows Vista and newer Windows versions defined by default also the system variable PSModulePath.
None of the predefined system variables with exception of PATH and PATHEXT should be deleted or modified ever as this could cause lots of troubles which can even result in Windows not starting anymore. I would strongly recommend using a virtual machine on experimenting with the predefined system variables for which a backup of the entire virtual machine image exists before starting the experiments.
1.1 Backup of persistent stored variables
It is advisable to make a backup of the user and system variables before playing around with them by opening a command prompt window and running for example:
md C:\VariablesBackup 2>nul
%SystemRoot%\System32\reg.exe EXPORT HKCU\Environment "C:\VariablesBackup\UserVariables.reg"
%SystemRoot%\System32\reg.exe EXPORT "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "C:\VariablesBackup\SystemVariables.reg"
1.2 Restore of persistent stored variables
Restoring the user variables can be done from within a command prompt window on having made a backup before with:
%SystemRoot%\System32\reg.exe DELETE "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /f
%SystemRoot%\System32\reg.exe IMPORT "C:\VariablesBackup\UserVariables.reg"
Restoring the system variables can be done from within a command prompt window opened as administrator on having made a backup before with:
%SystemRoot%\System32\reg.exe DELETE HKCU\Environment /f
%SystemRoot%\System32\reg.exe IMPORT "C:\VariablesBackup\SystemVariables.reg"
It is advisable to restart Windows on having restored user and system variables from a backup to make sure that really all processes use the restored variables.
1.3 Modification of PATH with a batch file
Batch file programmers thinking about modification of user or system PATH with a batch file should read first:
What is the reason for "X is not recognized as an internal or external command, operable program or batch file"?
Why are other folder paths also added to system PATH with SetX and not only the specified folder path?
How can I use a .bat file to remove specific tokens from the PATH environment variable?
How to search and replace a string in environment variable PATH?
Adding the current directory to Windows path permanently
ATTENTION: It is an absolute NO GO - NEVER EVER to use command SETX with %PATH% in a batch file to modify user or system PATH variable.
The only reason to modify user or system PATH with a batch file on installation of a program (executable or script) is that this program is designed for being mainly used by users from the Windows command line. A program is bad designed if it requires that its directory or one of its subdirectories is in PATH to work at all. A program is very bad designed if it adds a folder path to system PATH left to the folder paths defined by default by Windows.
The system PATH variable should start always with:
%SystemRoot%\System32;%SystemRoot%;%SystemRoot%\System32\Wbem
The Windows system directory is the directory containing most executables and dynamic linked libraries. For that reason it should be always the first directory searched for executables and libraries after the current directory.
2. Windows shell variables
There are a lot more Windows environment variables predefined as it can be seen on:
SS64: How-to: Windows Environment Variables
Wikipedia: Environment variable
These variables are defined by Windows shell which is by default explorer.exe being started on Windows start as Windows shell according to registry value Shell under registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
The Window shell elements noticed most by users are the Windows desktop, the Windows Start menu and the Windows taskbar with the system tray.
The Windows shell defines in its memory lots of environment variables depending on various values in Windows registry for the current user account not stored persistent in Windows registry as described above. The current list of environment variables is copied whenever a new process is created like starting an executable from Windows shell.
The list of environment variables defined by Windows shell consisting of the persistent stored user and system variables and the shell variables for current user account can be seen by opening a command prompt window and running the command SET without any additional argument.
Most of the shell variables are defined from the registry strings under
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Most of the registry string values exist in User Shell Folders of type REG_EXPAND_SZ and in Shell Folders of type REG_SZ. But there are some registry string values existing only under one of the two registry keys.
See also: How to create a directory in the user's desktop directory?
In this answer is explained in detail how Windows Explorer evaluates these registry string values and handles them on manual modification by a user using regedit.exe or reg.exe on the example of the shell folder Desktop.
The function CreateEnvironmentBlock and the private shell32 function RegenerateUserEnvironment are used by explorer.exe with GetUserNameExW and GetComputerNameExW to create the environment variables list copied by Windows Explorer on starting an executable from Windows shell using the Windows kernel library function CreateProcess.
See also the comments written by Eryk Sun on question Where are the environment variables for cmd.exe stored?
There are some environment variables on 64-bit Windows which depend on starting a 64-bit or a 32-bit executable. Microsoft documented them on WOW64 Implementation Details as follows:
64-bit process:
PROCESSOR_ARCHITECTURE=AMD64 or PROCESSOR_ARCHITECTURE=IA64 or PROCESSOR_ARCHITECTURE=ARM64
ProgramFiles=%ProgramFiles%
ProgramW6432=%ProgramFiles%
CommonProgramFiles=%CommonProgramFiles%
CommonProgramW6432=%CommonProgramFiles%
Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: The ProgramW6432 and CommonProgramW6432 environment variables were added starting with Windows 7 and Windows Server 2008 R2.
32-bit process:
PROCESSOR_ARCHITECTURE=x86
PROCESSOR_ARCHITEW6432=%PROCESSOR_ARCHITECTURE%
ProgramFiles=%ProgramFiles(x86)%
ProgramW6432=%ProgramFiles%
CommonProgramFiles=%CommonProgramFiles(x86)%
CommonProgramW6432=%CommonProgramFiles%
The value of the environment variable PROCESSOR_ARCHITECTURE cannot be used to find out from within a batch file if the installed Windows is a 32-bit (x86) or a 64-bit (AMD64) Windows. The value depends on processing of the batch file by either 64-bit %SystemRoot%\Sysem32\cmd.exe or by 32-bit %SystemRoot%\SysWOW64\cmd.exe on 64-bit Windows.
See also the Microsoft documentations:
File System Redirector
Registry Redirector
The usage of environment variables defined by the Windows shell must be done wisely on writing a batch file which should be designed for execution by other accounts on same or a different Windows machine. Many batch files working fine in environment of author of the batch file do not work in environment set up on running the same batch file as scheduled task with system account or on a different machine because of the differences in environment variables list.
The environment variables defined by a process on starting an executable with the Windows kernel library function CreateProcess determine the environment variables which the started executable can use.
Most applications use CreateProcess with value null for the parameter lpEnvironment. Therefore CreateProcess makes a copy of the current environment variables of the current process. For that reason every executable started from Windows desktop, start menu or taskbar gets the environment variables as defined by the explorer.exe instance running as Windows shell.
A really very good coded executable or script using environment variables which are defined by default on Windows verify explicitly that every used environment variable is really defined and use otherwise a suitable default value like C:\Windows on environment variable SystemRoot is not defined with checking if there is really a directory C:\Windows and exit with an appropriate error message on an important environment variable not defined before causing perhaps damage.
SystemRoot is an example for a Windows shell variable defined by explorer.exe as environment variable which is not determined by the registry string values of the shell folders. Some environment variable values should not be modified by a user at any time independent on its real source which no script author ever needs to know as being a Windows implementation detail.
3. Dynamic variables of Windows command processor
There are some variables listed in help of command SET output on running in a command prompt window set /? which cannot be found in the list of environment variables on running just set.
These variables are:
CD
DATE
TIME
RANDOM
ERRORLEVEL
CMDEXTVERSION
CMDCMDLINE
HIGHESTNUMANODENUMBER
The dynamic variables are internal variables of cmd.exe. So these variables are available only in a Windows command prompt window which is a running cmd.exe process or a batch file processed by cmd.exe. The dynamic variables are not available in other executables or scripts as these variables are not environment variables.
The most common used dynamic variables are:
CD
The current directory path not ending with a backlash, except the current directory is the root directory of the drive.
DATE
The current local date in format as defined for the account in region and language settings of Windows.
TIME
The current local time in format as defined for the account in region and language settings of Windows.
ERRORLEVEL
Exit value of previously executed command or program.
RANDOM
A random decimal number between 0 and 32767.
There are some more dynamic variables, but they are rarely used in batch files.
There is additionally the variable __AppDir__ containing the path of currently running cmd.exe always ending with a backslash which is not documented by Microsoft. I recommend not using this undocumented variable as there is no guarantee that a future version of cmd.exe still has this variable. __AppDir__ is on 64-bit Windows, for example, %SystemRoot%\System32\ on 64-bit %SystemRoot%\System32\cmd.exe currently running, or %SystemRoot%\SysWOW64\ on 32-bit %SystemRoot%\SysWOW64\cmd.exe currently running, or any other path on copying cmd.exe to any other folder and starting this copy of cmd.exe. There are some more undocumented dynamic variables listed on SS64 page How-to: Windows Environment Variables which should be used only with special care for the same reason.
The values of the most used dynamic variables are changed dynamically by the Windows command processor itself while the values of the environment variables change only if command SET is used during execution of a batch file to redefine an environment variable. This is an important difference between environment and dynamic variables.
4. Accessing the values of dynamic variables
Every environment variable can be deleted or redefined in the local environment of a processed batch file. There is no environment variable read-only.
cmd.exe contains internally in its code the file extension list .COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC which is used as value for PATHEXT if the environment variable PATHEXT does not exist in local environment variables list to be able to find nevertheless scripts and executables specified on command line or in a batch file without file extension. But cmd.exe does not contain a list of folder paths as fallback list if the environment variable PATH does not exist in local environment. So a batch file writer should be careful on modification of local environment variable PATH for whatever reason.
The values of the dynamic variables are referenced like the values of the environment variables with %variable% or !variable! on enabled delayed expansion. But the Windows command processor always searches first in the current list of environment variables if there is a variable with specified name. Only if there is no environment variable with that name, cmd searches next in its internal list of dynamic variables if there is one with the specified name.
The current value of a dynamic variable cannot be accessed anymore on definition of an environment variable with same name as a dynamic variable. For that reason a batch file writer should never use one of the dynamic variable names as name for an environment variable.
Here is a code which demonstrates what happens if a batch file writer has the really not good idea to define an environment variable with name ERRORLEVEL.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
echo/
echo Define environment variable ERRORLEVEL with string value "014".
echo/
set ERRORLEVEL=014
if %ERRORLEVEL% EQU 12 (echo EQU: ERRORLEVEL is 12.) else echo EQU: ERRORLEVEL is not 12.
if %ERRORLEVEL% == 014 (echo ==: ERRORLEVEL is 14.) else echo ==: ERRORLEVEL is not 14.
if errorlevel 0 (
if not errorlevel 1 (echo IF: ERRORLEVEL is 0.) else echo IF: ERRORLEVEL is greater than 0.
) else echo IF: ERRORLEVEL is less than 0.
echo/
echo Delete the environment variable ERRORLEVEL.
echo/
set ERRORLEVEL=
if %ERRORLEVEL% EQU 12 (echo EQU: ERRORLEVEL is 12.) else echo EQU: ERRORLEVEL is not 12.
if %ERRORLEVEL% == 014 (echo ==: ERRORLEVEL is 14.) else echo ==: ERRORLEVEL is not 14.
if errorlevel 0 (
if not errorlevel 1 (echo IF: ERRORLEVEL is 0.) else echo IF: ERRORLEVEL is greater than 0.
) else echo IF: ERRORLEVEL is less than 0.
echo/
echo In all test cases the value of dynamic variable ERRORLEVEL was 0.
echo/
for %%I in (%CMDCMDLINE%) do if /I "%%~I" == "/c" pause & goto EndBatch
:EndBatch
endlocal
The output of this batch file is:
Define environment variable ERRORLEVEL with string value "014".
EQU: ERRORLEVEL is 12.
==: ERRORLEVEL is 14.
IF: ERRORLEVEL is 0.
Delete the environment variable ERRORLEVEL.
EQU: ERRORLEVEL is not 12.
==: ERRORLEVEL is not 14.
IF: ERRORLEVEL is 0.
In all test cases the value of dynamic variable ERRORLEVEL was 0.
It can be seen on debugging the batch file that the first IF condition if %ERRORLEVEL% EQU 12 results in replacing %ERRORLEVEL% by the string 014 because of there is the environment variable ERRORLEVEL defined with this string value. The function wcstol is used by command IF because of operator EQU to convert the string 014 being interpreted as octal number because of the leading 0 and the string 12 to 32-bit signed integer values and compare them on equality. Therefore the first condition is true.
The second IF condition if %ERRORLEVEL% == 014 results also in replacing %ERRORLEVEL% by the string 014 because of there is the environment variable ERRORLEVEL defined with this string value. But now the function lstrcmpW is used by command IF because of operator ==. Therefore the second condition is also true.
The third IF condition uses the recommended syntax explained by help of command IF output on running if /? in a command prompt window. It can be seen that using the recommended syntax results in evaluation of the value of the internal dynamic variable ERRORLEVEL of Windows command processor even if an environment variable is defined with name ERRORLEVEL. The recommended syntax to evaluate the exit code of a previously executed command or program works always and anywhere within a batch file as demonstrated here.
See also:
What are the ERRORLEVEL values set by internal cmd.exe commands?
Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?
Further it must be taken into account that accessing current value of a dynamic variable is always on variable reference being expanded by the Windows command processor and not real execution of the command or executable.
A code to demonstrate that difference:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /L %%I in (1,1,3) do (
echo %%DATE%% %%TIME%% is: %DATE% %TIME%
echo ^^!DATA^^! ^^!TIME^^! is: !DATE! !TIME!
echo %%RANDOM%%/^^!RANDOM^^!: %RANDOM%/!RANDOM!
if exist %SystemRoot%\System32\timeout.exe (
%SystemRoot%\System32\timeout.exe /T 3 /NOBREAK >nul
) else (
%SystemRoot%\System32\ping.exe 127.0.0.1 -n 4 >nul
)
)
for %%I in (%CMDCMDLINE%) do if /I "%%~I" == "/c" pause & goto EndBatch
:EndBatch
endlocal
The output is for example:
%DATE% %TIME% is: 31.01.2021 13:54:30,67
!DATA! !TIME! is: 31.01.2021 13:54:30,68
%RANDOM%/!RANDOM!: 18841/27537
%DATE% %TIME% is: 31.01.2021 13:54:30,67
!DATA! !TIME! is: 31.01.2021 13:54:33,12
%RANDOM%/!RANDOM!: 18841/16705
%DATE% %TIME% is: 31.01.2021 13:54:30,67
!DATA! !TIME! is: 31.01.2021 13:54:36,16
%RANDOM%/!RANDOM!: 18841/32668
%DATE% %TIME% results in printing to console window three times the same date/time because of these two variable references are expanded already by the Windows command processor on parsing the entire command block before command FOR is executed at all. %RANDOM% results in printing three times the same number for the same reason while !RANDOM! prints usually three different numbers.
See also:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Variables are not behaving as expected
if errorlevel number and if not errorlevel number work also within a command block!
The dynamic environment variables can be accessed only with command extensions enabled as by default. Otherwise Windows command processor emulates COMMAND.COM behavior (more or less) of MS-DOS and Windows 95/98/ME not supporting dynamic variables at all as demonstrated by this code:
#echo off
setlocal DisableExtensions DisableDelayedExpansion
echo/
echo With command extensions disabled:
echo/
echo Date/time is: %DATE% %TIME%
echo Current dir: "%CD%"
endlocal
setlocal EnableExtensions DisableDelayedExpansion
echo/
echo With command extensions enabled:
echo/
echo Date/time is: %DATE% %TIME%
echo Current dir: "%CD%"
echo/
for %%I in (%CMDCMDLINE%) do if /I "%%~I" == "/c" pause & goto EndBatch
:EndBatch
endlocal
The output is for example:
With command extensions disabled:
Date/time is:
Current dir: ""
With command extensions enabled:
Date/time is: 31.01.2021 14:17:42,92
Current dir: "C:\Temp\Development & Test!"
The values of dynamic variables can be only read. It is not possible to modify the value of a dynamic variable with command SET as it results in the definition of an environment variable with the name of a dynamic variable which takes precedence over the dynamic variable.

How to set a variable for the current OS session only

setx permanently modifies environment variables.
set only makes variables available during batch script duration.
Is there any way to set a variable in order to hold its value until the system is restarted?
E.g. in my batch file, I'm checking if the variable is set like this:
if %MYVAR% == 1 (
<block>
)
#ECHO Off
IF NOT EXIST q25244121.org GOTO done
:: reset to original
FOR /f "tokens=1*delims==" %%a IN (q25244121.org) DO (
IF DEFINED %%a FOR /f "tokens=1*delims==" %%p IN ('set %%a') DO (
IF "%%a"=="%%p" IF "%%b" neq "%%q" SET "%%a=%%b"
)
)
:: Delete if not originally defined
FOR /f "tokens=1*delims==" %%p IN ('set') DO (
FINDSTR /L /i /c:"%%p=" q25244121.org >NUL
IF ERRORLEVEL 1 SET "%%p="
)
:done
:: Record current settings
set>q25244121.org
EXIT /b
This may work for you. You would need to change the SET instructions hown in CAPS to SETX. The tempfile would no doubt also need to be placed in a file where the username is part of the name you'd use.
If you were to include this batch in your startup directory, then it should restore the last-saved environment variables' values.
So, on first logon, the current variables' values are stored. On subsequent logons, the environment would be restored to those last stored, regardless of whether a setx had been executed.
You would however need to change procedures. This will restore to a known state. If you actually wanted to setx a value or install some software which adds new environment-variable values or changes existing ones (PATH would be favourite here) then you'd need to run this routine first, make the changes, delete the save file and re-run this routine. Awkward, I'll admit - but it's a way to do it.
Oh - and remember to set your variable after having setx it. You could even write a setxX batch to setx then set (or vice-versa) the required variable.
You may do that via a Batch-JScript hybrid script that use JScript's WshShell.Environment method. The documentation specify that there are four types of environments: System, User, Volatile and Process, and that Volatile type "Applies to current logon session and is not saved between logoffs and restarts", that is exactly what you want:
#if (#CodeSection == #Batch) #then
#echo off
rem Define a persistent variable for the current OS session only via JScript
Cscript //nologo //E:JScript "%~F0" MYVAR "This is the value"
goto :EOF
#end
var colEnvVars = WScript.CreateObject("WScript.Shell").Environment("Volatile");
colEnvVars(WScript.Arguments(0)) = WScript.Arguments(1);
You must save previous code in a .bat file and execute it as any Batch file. Note that the variable defined this way is not available in the current cmd.exe window, but only in future cmd.exe windows opened in the same OS session.
Tested on Windows 8.1.
Ok I found a much simpler (and obviously less hacky) way than the JScript method, but it put us in the right direction with the volatile environment manipulation.
Simply use the reg command to manipulate the volatile environment in the registry :
reg add "HKCU\Volatile Environment" /v "%MYVAR%" /d "%MYVALUE%" /f
reg add both can create and update the value.
\f bypasses the overwrite confirmation.
Setting an empty data with \d "" is equivalent to deleting the global variable. You won't see it in the shell with set, but you can still see it being empty in the registry.
Here is a reference: https://ss64.com/nt/reg.html

Remove unwanted path name from %path% variable via batch

Scope: Windows XP or newer
Tools: Batch script
I need to be able to remove an unneeded path name from the system %PATH% variable. I know how to add a new path name to the system %PATH% variable, using a tool such as SETX.EXE, which also makes it immediately available within the existing CMD environment. It's probably a matter of using FIND and/or a FOR loop of some kind, but I'm not quite sure how to accomplish this. Here's a sample path statement...
%PATH% = C:\;C:\Program Files\Common Files\Java;C:\oracle\product\10.2.0\bin;C:\WINDOWS;C:\WINDOWS\system32;
From this, I need to be able to remove the full path name related to "oracle." So, in the above example, I need to be able to remove the "C:\oracle\product\10.2.0\bin" from the above path statement. Unfortunately, not only could the oracle path name be different than shown above, there could be multiple oracle path names and all need to be removed. I tried implementing the solution here...
How can I extract a full path from the PATH environment variable?
However, it just isn't working. The script wouldn't find the path name. Any help would be appreciated. Thank you.
This removes the substring C:\Program Files (x86)\Git\bin; from the PATH string and re-assigns:
set PATH=%PATH:C:\Program Files (x86)\Git\bin;=%
You might use this to see the change:
echo %PATH:C:\Program Files (x86)\Git\bin;=% | tr ; \n
Note: be exact on the substring. It's case-sensitive and slash-sensitive.
If you need to make it a persistent change use setx instead of set and open another console for changes to take effect.
setx /M PATH "%PATH:C:\Program Files (x86)\Git\bin;=%"
You can try something like this :
#echo off&cls
setlocal EnableDelayedExpansion
set $line=%path%
set $line=%$line: =#%
set $line=%$line:;= %
for %%a in (%$line%) do echo %%a | find /i "oracle" || set $newpath=!$newpath!;%%a
set $newpath=!$newpath:#= !
echo set path=!$newpath:~1!
I putted an echo to the last line. Check the result and If it's OK for you, remove it.
After trying SachaDee's answers I got errors with paths like
C:\Program Files (x86)
with brackets:
Program Files (x86)\Directory
gave me
Directorywas unexpected at this time. (no matter what time I tried it)
I added
set $line=%$line:)=^^)%
before the for-loop and
set $newpath=!$newpath:^^=!
after the loop (not sure if it is necessary)
#echo off
setlocal EnableDelayedExpansion
set path
set $line=%path%
set $line=%$line: =#%
set $line=%$line:;= %
set $line=%$line:)=^^)%
for %%a in (%$line%) do echo %%a | find /i "oracle" || set $newpath=!$newpath!;%%a
set $newpath=!$newpath:#= !
set $newpath=!$newpath:^^=!
set path=!$newpath:~1!
And it is now working.
I found the other solutions to this problem a bit awkward, I don't really want to rely on exact paths, complex 'delayed expansion' syntax, removing spaces for the 'for /f' loop and then adding them back in...
I think this is more elegant, and I commented the hell out of it so even someone new to the horrors of Batch can follow along.
::Turn off command display and allows environmental variables to be overridden for the current session
#echo off & setlocal
::Creates a unique file to use for the 'for loop'
set "TMPFILE="%temp%\tmp%RANDOM%%RANDOM%.txt""
::Duplicate PATH into OLDPATH
set "OLDPATH=%PATH%"
::Declare label for the 'goto' command
:Loop
::Extract the first text token with the default delimiter of semicolon
for /f "tokens=1 delims=;" %%G in ("%OLDPATH%") do (
REM Copy text token to TMPFILE unless what we want to remove is found
<NUL set /p="%%G" | find /i "StRiNgThAtMaTcHeSwHaTtOrEmOvE" >NUL 2>&1 || <NUL set /p="%%G;" >>%TMPFILE%
REM Remove text token from OLDPATH
set "OLDPATH=%OLDPATH:*;=%"
)
::Repeat loop until OLDPATH no longer has any delimiters, and then add any remaining value to TMPFILE
echo %OLDPATH% | findstr /C:";" >NUL && (goto :Loop) || <NUL set /p="%OLDPATH%" >>%TMPFILE%
::Set the path to TMPFILE
for /f "usebackq delims=" %%G in (%TMPFILE%) do (set "PATH=%%G")
::Clean-up
del %TMPFILE% >NUL 2>&1
::An echo and pause just for debug purposes
echo %PATH%
pause
I use this in CYGWIN to filter out CYGWIN paths before starting some Windows commands:
export PATH=`perl -e '#a=grep {$_ =~ /^\/cygdrive\//} split(":", $ENV{PATH});print join(":",#a)'`
I'm quite sure it's easy to adapt to Windows-native perl and bat files. Advantage: the flexible power of regular expressions.
I wanted to remove %LocalAppData%\Microsoft\WindowsApps; from PATH. But this was not possible due to using a another variable in the environment variable for Windows. The CALL hack is worked in SS64. (Also, thanks to Jens A. Koch for the base command.)
CALL set PATH=%PATH:%LocalAppData%\Microsoft\WindowsApps;=%
Of course, the PATH changing by SET will not be permanent. For fixed change, it is necessary to use the SETX command or directly change the entries in the Registry.
Actually, this solution was not needed to delete %LocalAppData%\Microsoft\WindowsApps; from PATH.
The %LocalAppData%\Microsoft\WindowsApps; is stored in the PATH entry of the Registry's HKCU\Environment key. Although it is more practical to delete this entry with the REG DELETE command, if there are another directories in the PATH entry, they will also be deleted, so new solution is needed.
I failed to remove the %USERPROFILE% variable syntax from SET (The %% symbol dilemma). Fortunately, PShell came to the rescue:
SET userprofile=
Powershell -c "$UserEnvironmentPath = [System.Environment]::GetEnvironmentVariable('Path', 'User'); $UserEnvironmentPath = $UserEnvironmentPath.Replace('%USERPROFILE%\AppData\Local\Microsoft\WindowsApps;',''); [Microsoft.Win32.Registry]::SetValue('HKEY_CURRENT_USER\Environment', 'Path', $UserEnvironmentPath, [Microsoft.Win32.RegistryValueKind]::ExpandString)"
Special thanks to vonpryz for the last command. Because PowerShell's [System.Environment]::SetEnvironmentVariable command saves variables to Registry as REG_SZ even if their original value type is REG_EXPAND_SZ, which it's the known issue.
I wrote this code to simply remove any python executeable path from the path variable,
and insert my own specefic python version in the path so i can run python with
the versoin i wanted.
setlocal enableDelayedExpansion
set path`enter code here`
set $line=%path%
set $line=%$line: =#%
set $line=%$line:;= %
set $line=%$line:)=^^)%
set newpath=
for %%a in (%$line%) do (
echo %%a | find /i "python" ||set newpath=!newpath!;%%a
)
set path=!newpath!
set PATH=D:\python2.7\;%PATH%
#REM Rest of your script
python --version
#REM to exit the batch but not the window
exit /b
also, the first line is important! don't remove it or it wont work.
Notice: this code must run from a batch ".bat" file , if u want to copy paste this code in cmd window, you must replace all "%%a" to "%a" in this code.
If you know a file that exists within the directory you want to remove (e.g. want to remove all paths that might include java.exe), the following will work very straightforwardly by simply doing string replacement, no need to parse the path, etc:
#REM Executable to look for
set CMD=java.exe
:search
#REM Find the executable anywhere in the path
for %%a in (%CMD%) do set FOUND=%%~$PATH:a
if "%FOUND%"=="" goto done
#REM Strip \cmd.ext so we just have the directory
set FOUND=!FOUND:\%CMD%=!
#echo Found %CMD% in %FOUND%
#echo Removing %FOUND% from path...
set "PATH=!PATH:%FOUND%=!"
#REM Clean up any lone leftover \ in the path (in case the path was C:\foo\ instead of C:\foo)
set PATH=%PATH:;\;=;%
goto search
:done

Windows path doesn't contain C:\WINDOWS\system32, can I add it using batch script?

I am writing a batch script that, quite reasonably, depends on "C:\WINDOWS\system32" being part of the PATH environment variable. I recently encountered a (developer's) machine that had a really weird path that didn't include system32, and therefore my batch script didn't work.
I looked up ways for my batch script to check the PATH variable and add system32 if it is not there. However, the solution I found used setx which ironically enough ALSO depends on system32 in the PATH variable. Are there any programmatic ways to add system32 to the PATH without it already being there?
Also please let me know if this is such an edge case that it doesn't make sense to make my script robust against it. I'm not expecting any of my typical users to have such a borked PATH variable. Should I bother?
try this:
for /f "delims=" %%a in ("%comspec%") do set "PATH=%PATH%;%%~DPa"
or this:
for /f "delims=" %%a in ("%comspec%") do set "compath=%%~DPa"
set "PATH=%PATH%;%compath:~0,-1%"
#ECHO OFF
SETLOCAL
SET "required=c:\windows\system32"
for %%p in ("%path:;=" "%") do (
FOR %%j IN ("" \) DO (
IF /i %%p=="%required%%%~j" GOTO :nextstep
)
)
SETx PATH "%required%;%path%"
:nextstep
ECHO PATH=%path%
GOTO :EOF
Here's my take on the problem. PATH may contain the required directory with or without a trailing \, and it may or may not have a preceding or trailing ;
I'd suggest that you examine the operation of SETX however. It sets an environment variable for FUTURE cmd sessions, not the CURRENT or EXISTING sessions, AFAIAA....and perhaps not for PATH or some other variables (I tried setting PATH using the SETX in the above batch - future sessions did NOT acquire the new value set, but it appeared to be set according to regedit32 - perhaps it needs a reboot - haven't investigated further at this stage)
Note that the above will need to have the required directory appended to %path% if my observations are borne out...

Setting a variable from an executable

I am running an executable in a batch file with two parameters;
cmd /k ""executable" "param1" "param2""
This returns a string that I want to launch. I can't figure out how to set this return in a variable and subsequently launch it in IE.
Any ideas?
If the returned string contains a single line you may use FOR /F to set the value of an environment variable. For example:
s1.cmd
echo this is a one line string
s2.cmd
#SETLOCAL
#ECHO OFF
for /f "tokens=*" %%a in ('cmd /c s1.cmd') do set MY_VAR=%%a
echo got: %MY_VAR%
ENDLOCAL
Result
C:\> s2.cmd
got: this is a one line string
C:\>
You can use the following syntax to capture the output of your executable into a variable:
FOR /F "tokens=*" %%i in ('%~dp0YOUR_APP.exe') do SET TOOLOUTPUT=%%i
Source
then you can pass the value on to IE like so:
START "YOUR_WINDOW_NAME" /MAX /D"C:\Program Files\Internet Explorer\" iexplore %TOOLOUTPUT%
I take it that the application code that determines the url is too complicated to be reproduced in a batch file directly, or the source to the executable has been lost. If not I personally would prefer to have the logic visible in the batch file itself.
start %1 %2
Edit: Romulo A. Ceccon posted a much better solution which doesn't involve any file system access and dirty tricks. Left this here for reference (it works with command.com as well if you need 9x compatibility), but please prefer Romulo's solution.
Go through an environment variable you set by using an intermediate helper script you dynamically generate from a template. You will need write permissions somewhere, otherwise it cannot be done (the Windows command shell language is very, very limited.)
Let's call your helper script template helper.tpl with the following contents:
set INTERMEDVAR=
Make sure that helper.tpl has only a single line (no trailing CRLF!) and make sure you don't have any spaces after the equals sign there.
Now, in your main script, capture the output from your command into a temporary file (let's call it my_output_file.tmp):
cmd /k ""executable" "param1" "param2"" > my_output_file.tmp
Then copy the contents of the helper template and the output together into your helper script, let's call it my_helper_script.cmd:
copy /b helper.tpl + my_output_file.tmp my_helper_script.cmd
Then evaluate the helper script in the current context:
call my_helper_script.cmd
Now the INTERMEDVAR variable is set to the first line of the output from "executable" (if it outputs more than one line, you're on your own...) You can now invoke IE:
start iexplore.exe "%INTERMEDVAR%"
And don't forget to clean up the created files:
del /q /f my_output_file.tmp my_helper_script.cmd
This will obviously not work when invoked multiple times in parallel - you'll have to parametrize the temporary file and helper script names using the current cmd.exe's PID (for example) so that they won't overwrite each other's output, but the principle is the same.
However, if you can get a real shell, use that. cmd.exe is extremely cumbersome.

Resources