I am working on a little project to deploy client applications in my company via GPO (Citrix Receiver and HDX Real Time Engine).
HDX Client can be installed only if Citrix Receiver has been installed beforehand. I am also testing whether HDX is already installed on the machine along with its version. See what I have done so far:
setlocal enabledelayedexpansion
REM Logs Share
set logshare=\\[path_to_logs_share]\
REM Search for Citrix Receiver Client
reg query HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432NODE\Citrix\PluginPackages\XenAppSuite\ICA_Client
REM If Client has been found - search for HDX Client starting by "Citrix HDX"
if %errorlevel% EQU 0 (
reg query HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall /s /v Displayname ^| findstr /c:"Citrix HDX"
REM If HDX Client has been detected set a variable containing the version of it
if !errorlevel! EQU 0 (
for /F "tokens=8" %%a in ('reg query HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall /s /v Displayname ^| findstr /c:"Citrix HDX"') do set HDX_Version=%%a
REM If HDX version is greater or eqaul to 2.4
if %HDX_Version% GEQ 2.4 (
echo Current version is ok >> %logshare%%ComputerName%.txt
) else (
echo Installation HDX 2.4 in progress >> %logshare%%ComputerName%.txt
)
) else (
REM In case HDX has not been detected at all - installation begins
echo Installation HDX 2.4 in progress >> %logshare%%ComputerName%.txt
)
) else (
REM In case Citrix Client is missing
echo Client Citrix missing
)
Endlocal
The issue is that testing %errorlevel% twice in a batch script is apparently awkward. I do not know how to work out that problem.
The line output by reg and findstr is for example:
DisplayName REG_SZ Citrix HDX RealTime Media Engine 2.4
The version at end of this registry string value must be processed to determine if an already installed Citrix client must be updated or nothing must be done.
In general it is better to use if not errorlevel 1 instead of if %errorlevel% EQU 0 or if !errorlevel! EQU 0 because this syntax works really everywhere. if not errorlevel 1 means IF exit code of previous command/application is NOT GREATER OR EQUAL 1 or in other words is LESS THAN 1 or is EQUAL 0 because nearly no command/application exits with a negative value according to the guidelines of Microsoft. This syntax working since MS-DOS inside and outside a command block is explained by help of command IF output on running in a command prompt window if /?.
The redirection operator | must be escaped with ^ only on being used inside set of command FOR. The usage of ^| on standard command line like on on second reg query command line results in getting the vertical bar interpreted as literal character and REG outputs an error message because of too many parameters.
But the main reason for code not working as expected is the line:
if %HDX_Version% GEQ 2.4
There is set HDX_Version=%%a inside the command block starting with ( on first IF line and ending with matching ) in last but one non-empty line which defines this environment variable with string read from Windows registry. The variable reference %HDX_Version% is replaced by Windows command processor on parsing the entire command block before running the first IF. So most likely %HDX_Version% is replaced by nothing and the IF condition executed is if GEQ 2.4 which results in an exit of batch file execution because of a syntax error. It would be necessary to use here also delayed environment variable expansion, i.e. use syntax !HDX_Version! on this IF command line.
However, the code would also not work with if !HDX_Version! GEQ 2.4 because of the comparison operators EQU, NEQ, GEQ, etc. are primary designed for comparing two 32-bit signed integer values. If one of the two arguments strings left and right the operator cannot be successfully converted to a 32-bit signed integer, cmd.exe runs a string comparison and compares the integer value returned by the string comparison function against value 0 on being equal, not equal, greater than, etc. Floating point values containing . are not supported by cmd.exe at all. For more details see answer on Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files.
I suggest the following code for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LogShare=\\[path_to_logs_share]\"
if not exist "%LogShare%" set "LogShare=%TEMP%\"
set "MinimumMajorVersion=2"
set "MinimumMinorVersion=4"
set "SoftwareKey=HKEY_LOCAL_MACHINE\SOFTWARE"
if not "%ProgramFiles(x86)%" == "" if not exist %SystemRoot%\Sysnative\cmd.exe set "SoftwareKey=%SoftwareKey%\Wow6432Node"
rem Search for Citrix receiver client.
%SystemRoot%\System32\reg.exe query %SoftwareKey%\Citrix\PluginPackages\XenAppSuite\ICA_Client >nul 2>nul
if errorlevel 1 (
echo Citrix client is not installed.>>"%LogShare%%ComputerName%.txt"
goto InstallClient
)
rem Search for HDX client starting by "Citrix HDX" if receiver client was found.
for /F "tokens=8" %%I in ('%SystemRoot%\System32\reg.exe query %SoftwareKey%\Microsoft\Windows\CurrentVersion\Uninstall /s 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R /C:"DisplayName.*Citrix HDX"') do set "HDX_Version=%%I" & goto EvaluateVersion
echo HDX version not found under registry key %SoftwareKey%\Microsoft\Windows\CurrentVersion\Uninstall.>>"%LogShare%%ComputerName%.txt"
goto InstallClient
:EvaluateVersion
for /F delims^=.0123456789^ eol^= %%I in ("%HDX_Version%") do (
echo Determined HDX version string "%HDX_Version%" is of unknown format.>>"%LogShare%%ComputerName%_Error.txt"
goto EndCitrixCheck
)
for /F "tokens=1,2 delims=." %%I in ("%HDX_Version%") do (
if %%I LSS %MinimumMajorVersion% (
echo Determined HDX version %HDX_Version% is too low.>>"%LogShare%%ComputerName%.txt"
goto InstallClient
)
if %%I EQU %MinimumMajorVersion% (
if "%%J" == "" (
if not %MinimumMinorVersion% == 0 (
echo Determined HDX version %HDX_Version% has no minor version number.>>"%LogShare%%ComputerName%.txt"
goto InstallClient
)
) else if %%J LSS %MinimumMinorVersion% (
echo Determined HDX version %HDX_Version% is too low.>>"%LogShare%%ComputerName%.txt"
goto InstallClient
)
)
echo Determined HDX version %HDX_Version% is okay.>>"%LogShare%%ComputerName%.txt"
goto EndCitrixCheck
)
echo Determined HDX version string "%HDX_Version%" is of unknown format.>>"%LogShare%%ComputerName%_Error.txt"
goto EndCitrixCheck
:InstallClient
echo Installation of HDX in progress ...>>"%LogShare%%ComputerName%.txt"
rem Add here the command lines to install the Citrix client.
:EndCitrixCheck
if "%TEMP%\" == "%LogShare%" del "%LogShare%%ComputerName%.txt"
endlocal
The first and last IF condition in this code are just for making it possible for everyone reading this code to run it without an error message.
This batch code works even on Windows XP although this is most likely not a requirement for this task.
The Windows x86 on Windows x64 emulation must be taken into account on accessing registry keys under HKEY_LOCAL_MACHINE\SOFTWARE according to the Microsoft articles:
WOW64 Implementation Details
File System Redirector
Registry Keys Affected by WOW64
The environment variable SoftwareKey is defined first with standard registry key HKEY_LOCAL_MACHINE\SOFTWARE. This is the right key for 32-bit Windows and batch file being executed in 32-bit environment by the x86 versions of cmd.exe and reg.exe executed from %SystemRoot%\SysWOW64. But it is necessary to append \Wow6432Node to access the right key on batch file being executed by x64 version of cmd.exe starting x64 version of reg.exe stored both in %SystemRoot%\System32 on 64-bit Windows.
The rewritten code avoids definition/modification of an environment variable in a command block referenced in same command block. Therefore delayed environment variable expansion is not needed by this code which solves first main problem.
Let us look on the long command line:
for /F "tokens=8" %%I in ('%SystemRoot%\System32\reg.exe query %SoftwareKey%\Microsoft\Windows\CurrentVersion\Uninstall /s 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R /C:"DisplayName.*Citrix HDX"') do set "HDX_Version=%%I" & goto EvaluateVersion
FOR executes in a separate command process started with cmd.exe /C and the string in the round brackets between the two ' in background for example the command line:
C:\Windows\System32\reg.exe query HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall /s 2>nul | C:\Windows\System32\findstr.exe /I /R /C:"DisplayName.*Citrix HDX"
REG outputs everything of uninstall registry key for x86 applications to handle STDOUT. An error message output to handle STDERR would be redirected with 2>nul to device NUL to suppress it. REG should not output an error message in this case. It is possible to use additionally /v DisplayName in addition to /s on Windows Vista and later Windows versions to get output by REG just all values with name DisplayName. The output of REG is redirected with | to handle STDIN of command FINDSTR.
FINDSTR searches in every line case-insensitive with a regular expression for a string starting anywhere in line with DisplayName, having 0 or more characters and the string Citrix HDX. The usage of /R /C:"DisplayName.*Citrix HDX" instead of just "DisplayName.*Citrix HDX" is necessary as otherwise FINDSTR would run a regular expression find searching for DisplayName and 0 or more characters and the string Citrix OR the string HDX anywhere in a line which is not wanted here. FINDSTR outputs hopefully always just the line with the string value of interest to handle STDOUT of separate command process.
Read the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul and |. The redirection operators > and | must be escaped with caret character ^ on FOR command line to be interpreted as literal characters when Windows command interpreter processes this command line before executing command FOR which executes the embedded command line with reg and findstr with using a separate command process started in background.
FOR captures the output written to STDOUT of started command process and processes it line by line with ignoring empty lines and by default also lines starting with a semicolon which both do not occur here. Other lines are split up into substrings (tokens) using normal space and horizontal tab as delimiters and assigns just eight space/tab delimited substring to specified loop variable I because of option tokens=8. FOR runs never the command SET if there is no line with at least eight space/tab separated strings. The string assigned to loop variable I is assigned as is to environment variable HDX_Version and the batch file execution continues on the line below label EvaluateVersion.
The second FOR validates if the string assigned to HDX_Version consists of only one or more dots/digits. FOR outputs an error message to an error file instead of standard text file in case of the string assigned to HDX_Version contains any other character than .0123456789 including a ; at beginning of the string. The execution of the batch file continues at end of batch file as this error condition cannot be handled automatically. It could be that the displayed string changed since writing this batch file which should be at least detected and reported by the batch file.
Otherwise on HDX version being most likely of format major.minor one more FOR is used to split up the version into two strings which are integers for evaluation with the integer comparators of command IF. The minor version number must not exist except the major version number is equal the minimum major version number. The missing minimum minor version number is in this case interpreted as 0 and so an installation/update of Citrix client is necessary, too.
It is very unlikely, but nevertheless possible that the string assigned to environment variable HDX_Version consist of only one or more . in which case the third FOR does not execute any command line in command block. This results also in writing an error message into the error file and a jump to end of batch file.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
goto /?
if /?
reg /?
reg query /?
rem /?
set /?
setlocal /?
Related
I have the batch file below:
FOR /F "delims=|" %%I IN ('DIR "%C:\TeamCity\buildAgent\work\53bba593f5d69be\public\uploads\*.xlsx" /B /O:D') DO SET NewestFile=%%I
FOR /F "delims=" %%a IN ('wmic OS Get localdatetime ^| find "."') DO SET DateTime=%%a
set Yr=%DateTime:~0,4%
set Mon=%DateTime:~4,2%
set Day=%DateTime:~6,2%
setlocal enableDelayedExpansion
set "baseName=InventoryReport%Yr%-%Mon%-%Day% V1.%n%"
set "n=0"
FOR /f "delims=" %%F in (
'DIR /b /ad "%baseName%*"^|findstr /xri "\\192.168.0.141\Medisun\28 - Business Development\30 - Product Inventory\InventoryReport\"%baseName%[0-9]*""'
) do (
set "name=%%F"
set "name=!name:*%baseName%=!"
if !name! gtr !n! set "n=!name!"
)
set /a n+=1
md "%baseName%%n%"
copy "%C:\TeamCity\buildAgent\work\53bba593f5d69be\public\uploads\%NewestFile%" "\\192.168.0.141\Medisun\28 - Business Development\30 - Product Inventory\InventoryReport\%baseName%%n%.xlsx"
cmd /k
I cannot get it to find the greatest version number of previously copied file between V1. and file extension .xlsx in file name and increment it but one. The batch file finds the file V1.1, but overwrites it instead of copying newest file with V1.2 in target file name.
How can I get the previous file version first and increment that number?
The file copying task can be done with following batch file:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFolder=C:\TeamCity\buildAgent\work\53bba593f5d69be\public\uploads"
set "TargetFolder=\\192.168.0.141\Medisun\28 - Business Development\30 - Product Inventory\InventoryReport"
for /F "eol=| delims=" %%I in ('dir "%SourceFolder%\*.xlsx" /A-D /B /O-D 2^>nul') do set "NewestFile=%%I" & goto CheckTarget
echo ERROR: Found no *.xlsx file in the folder:
echo "%SourceFolder%"
exit /B 1
:CheckTarget
if not exist "%TargetFolder%\" md "%TargetFolder%\" 2>nul
if exist "%TargetFolder%\" goto GetDateTime
echo ERROR: Failed to access or create the folder:
echo "%TargetFolder%"
exit /B 2
:GetDateTime
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "DateTime=%%I"
set "BaseName=InventoryReport%DateTime:~0,4%-%DateTime:~4,2%-%DateTime:~6,2% V1"
set "FileNumber=-1"
setlocal EnableDelayedExpansion
for /F "tokens=2 delims=." %%I in ('dir "!TargetFolder!\!BaseName!.*.xlsx" /A-D /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R /X /C:"!BaseName!\.[0123456789][0123456789]*\.xlsx"') do if %%I GTR !FileNumber! set "FileNumber=%%I"
endlocal & set "FileNumber=%FileNumber%"
set /A FileNumber+=1
copy /B /V "%SourceFolder%\%NewestFile%" "%TargetFolder%\%BaseName%.%FileNumber%.xlsx" >nul || exit /B 3
endlocal
The first FOR loop runs in background one more command process with %ComSpec% /c and the command line between the round brackets appended as additional arguments. So executed is with Windows installed to C:\Windows in background:
C:\Windows\System32\cmd.exe /c dir "C:\TeamCity\buildAgent\work\53bba593f5d69be\public\uploads\*.xlsx" /A-D /B /O-D 2>nul
The background command process executes internal command DIR which
searches in the specified directory
just for file names because of option /A-D (attribute not directory)
matching the wildcard pattern *.xlsx and
outputs them in bare format with just file name + extension because of option /B
ordered reverse by last modification date because of option /O-D which means the file name of newest file is output first and the file name of the oldest file is output last.
It is possible that either the source directory does not exist at all or the source directory does not contain any file matching these criteria. DIR would output in this case an error message to handle STDERR of background command process which would be redirected by the command process processing the batch file to own handle STDERR and so displayed most likely in console window. This error message is not wanted as there is a better one output below the FOR loop if there is not found any file for copying. For that reason the error message is redirected already by background command process to device NUL to suppress it.
Read the Microsoft documentation about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
FOR captures everything written to handle STDOUT of background command process and processes this captured output line by line after the executed background cmd.exe terminated itself.
FOR with option /F ignores always empty lines which do not occur in this case. Every other line would be first split up into substrings using normal space and horizontal tab character as delimiters. The line would be ignored if the first space/tab delimited string starts with default end of line character ; (semicolon). Otherwise just the first space/tab delimited string would be assigned to loop variable I and the command respectively command block would be executed next.
A *.xlsx file name can contain one or more spaces. For that reason the FOR option delims= is used to define an empty list of string delimiters to disable line splitting completely. It is unusual, but nevertheless possible, that a file name starts with a semicolon. Therefore FOR option eol=| is also used to define the vertical bar as end of line character which no file name can contain as described by Microsoft in the documentation about Naming Files, Paths, and Namespaces. So the result is that every file name output by DIR in background command process is assigned one after the other completely to the loop variable I.
The file name of the newest file is output first and so its name is assigned to environment variable NewestFile. Then the first FOR loop is exited with using command GOTO to jump to the first line below label CheckTarget as processing the other file names would be a waste of time and CPU power.
There is a meaningful error message output on no *.xlsx file found to copy and batch file processing is exited with exit code 1 to indicate an error condition to parent process starting this batch file.
Next, with having file name of newest file in source folder, an existence check of target folder is done with creating the target folder if not already existing. A meaningful error message is output if the target folder is still not existing because of other computer or storage device is not running or is not reachable at all or the creation of the target folder failed for whatever reason.
The next two command lines get the current date/time in a region independent format and define the base file name for target file using the current date. For a full description of these two lines see my answer on Time is set incorrectly after midnight.
Then the file number is defined with value -1 and delayed expansion is enabled as required for the number comparison done by the next FOR loop.
The third FOR loop is similar to first FOR loop. There is additionally the output of command DIR redirected to handle STDIN of FINDSTR to be filtered for verification if the file name of found file contains really just one or more digits between the dot after V1 and the dot of the file extension, i.e. this part of the file name is a valid number. It can be assumed that FINDSTR outputs the same lines as output by DIR on target folder not used for something different than the Excel files with the defined pattern for the file name. The two dots in name of each file must be escaped with a backslash in case-insensitive interpreted regular expression search string on which the space character is interpreted as literal character because of using /C: and /R and not as OR expression as on omitting /C:. For 100% safety on processing later only correct file names /X is additionally used to output only file names on which entire file name is matched by the search expression.
This time the FOR loop should not assign the entire file name to loop variable I. There is of interest only the string between the first dot after V1 and the file extension .xlsx. For that reason the FOR option delims=. is used to split the file names on dots and option tokens=2 is used to instruct command FOR to assign the second dot delimited string to loop variable I which is the incremented file number.
A simple integer comparison is done to determine if the file number of current file name is greater than file number assigned currently to environment variable FileNumber in which case this greater file number is assigned to the environment variable FileNumber.
The local environment with enabled delayed expansion is no longer needed after knowing the greatest file number of the existing files if there is one at all. So this environment is destroyed which would mean the environment variable FileNumber would have again the number -1 as assigned to the environment variable in initial environment. Please read this answer for details about the commands SETLOCAL and ENDLOCAL. So to pass the current value of FileNumber in current environment to FileNumber in previous environment the command line with endlocal contains additionally the command set "FileNumber=%FileNumber%" which is processed by cmd.exe, for example, to set "FileNumber=12" before executing the command ENDLOCAL. That simple trick is used to pass the greatest file number value to FileNumber in previous environment.
See also:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Single line with multiple commands using Windows batch file
The greatest file number of an existing file or -1 is incremented by one before copying the newest file in source folder with this number and current date in file name to the target folder with verification that the file data were really correct written on target storage media.
The batch file is exited with exit code 3 in case of file copying failed for whatever reason.
Finally the batch file processing ends with explicitly restoring initial execution environment. The last command ENDLOCAL would be not really necessary because of Windows command processor runs it implicit on exiting processing of this batch file as done for example on execution of one of the three commands exit /B.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
copy /?
dir /?
echo /?
endlocal /?
exit /?
findstr /?
for /?
goto /?
set /?
setlocal /?
wmic /?
wmic os /?
wmic os get /?
wmic os get localdatetime /?
PS: The greatest possible file number is 2147483647. But a day has only 86400 seconds and more than 65535 files in one directory would be a real problem, too. So the maximum file number 2147483647 should be never reached if no user renames a file in target folder to exceed that maximum number.
This question already has answers here:
Arrays, linked lists and other data structures in cmd.exe (batch) script
(11 answers)
Closed 3 years ago.
I have a list of paths from which I want to extract folder name
I wrote:
#echo off
set paths[0]="C:\p\test1"
set paths[1]="C:\p\test2"
set paths[2]="C:\p\test3"
(for %%p in (%paths%) do (
for %%F in (%%p) do echo Processing %%~nxF
))
but seems that nothing is shown.
I expected to see:
Processing test1
Processing test2
Processing test3
It makes a big difference if first " is specified on a set command line left to variable name or left to variable value. In most cases it is better to specify it left to the variable name, especially if a variable value holding a path should be concatenated later with a file name to a full qualified file name.
See also: Why is no string output with 'echo %var%' after using 'set var = text' on command line?
The solution for this task is:
#echo off
set "paths[0]=C:\p\test1"
set "paths[1]=C:\p\test2"
set "paths[2]=C:\p\test3"
for /F "tokens=1* delims==" %%I in ('set paths[ 2^>nul') do echo Processing %%~nxJ
The command FOR with option /F and a set enclosed in ' results in starting one more command process running in background with %ComSpec% /c and the command line specified between the two ' appended as further arguments. So executed is in this case with Windows installed to C:\Windows:
C:\Windows\System32\cmd.exe /c set paths[ 2>nul
The command SET outputs all environment variables of which name starts with paths[ line by line using the format VariableName=VariableValue to handle STDOUT of started background command process.
It could be that there is no environment variable of which name starts with paths[ which would result in an error message output to handle STDERR by command SET which would be redirected from background command process to handle STDERR of the command process which is processing the batch file and for that reason would be displayed in console window. For that reason a possible error message is redirected by the background command process to device NUL to suppress it with using 2>nul.
Read the Microsoft article about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded set command line with using a separate command process started in background.
FOR captures in this case everything written to handle STDOUT of started background command process and process this output line by line after started cmd.exe terminated itself.
Empty lines are ignored by FOR which does not matter here as there are no empty lines to process.
FOR would split up a non-empty line into substrings using normal space and horizontal tab as string delimiters and would assign just first space/tab separated string to specified loop variable, if it does not start with default end of line character ;. This default line splitting behavior is not wanted here. For that reason the option delims== defines the equal sign as string delimiter.
The option tokens=1* instructs FOR to assign in this case the variable name to specified loop variable I and assign everything after the equal sign(s) after variable name without any further string splitting on equal signs to next loop variable according to ASCII table which is in this case J. That is the reason why loop variables are interpreted case-sensitive while environment variables are handled case-insensitive by the Windows command processor.
In this case only the variable value is of interest in the body of the FOR loop. For that reason just loop variable J is used on ECHO command line while I is not used at all.
The modifier %~nxJ results in removing surrounding double quotes from string value assigned to loop variable J and next get the string after last backslash or beginning of string in case of the string value does not contain a backslash at all. This is the name of the last folder in folder path string.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
for /?
set /?
UPDATE:
There is a big advantage of this solution in comparison to the other two solutions posted up to now here:
There is not used delayed environment variable expansion which is always problematic on working with file or folder names on not being 100% sure that no folder and no file contains ever an exclamation mark in its name.
Let us compare the three solutions with unusual folder names containing !.
#echo off
rem Make sure there is no environment variable defined of which name starts with
rem paths[ as suggested by Compo which is a very valuable addition on my code.
for /F "delims==" %%I in ('set paths[ 2^>nul') do set "%%I="
set "paths[0]=C:\p\test1!"
set "paths[1]=C:\p\!test2"
set "paths[2]=C:\p\!test!3"
echo/
echo Results of solution 1:
echo/
for /F "tokens=1* delims==" %%I in ('set paths[ 2^>nul') do echo Processing %%~nxJ
echo/
echo Results of solution 2:
echo/
SetLocal EnableDelayedExpansion
for /L %%i in (0,1,2) do (
for %%j in (!paths[%%i]!) do echo Processing %%~nxj
)
endLocal
echo/
echo Results of solution 3:
echo/
Setlocal EnableDelayedExpansion
Call :process paths "!paths[0]!" "!paths[1]!" "!paths[2]!"
Endlocal
echo/
pause
goto :EOF
:process
Set P_C=0
Set /a P_C-=1
For %%a in (%*) DO (
CALL :populate %1 "%%~a"
)
Set /a P_C-=1
For /L %%b in (0,1,!P_C!) DO (
ECHO Processing %1[%%b] = "!%1[%%b]!"
)
GOTO :EOF
:populate
Set "%1[!P_C!]=%~2"
Set /a P_C+=1
GOTO :EOF
The output on running this batch file is:
Results of solution 1:
Processing test1!
Processing !test2
Processing !test!3
Results of solution 2:
Processing test1
Processing test2
Processing 3
Results of solution 3:
Processing paths[0] = "C:\p\test1\p\\p\3"
Solution 1 as posted here works for all three folder names correct.
Solution 2 omits for first and second folder name the exclamation mark which will most likely cause errors on further processing. The third folder name is modified to something completely different. Enabled delayed expansion results in parsing a second time echo Processing %%~nxj after %~nxj being replaced by !test!3 with interpreting test in folder name now as environment variable name of which value is referenced delayed. There was no environment variable test defined on running this batch file and so !test!3 became just 3 before echo was executed by Windows command processor.
Solution 3 produces garbage on any folder name contains an exclamation mark, even on full qualified folder name defined before enabling delayed expansion and referenced with delayed expansion on calling the subroutine process.
Well, folder and file names with an exclamation mark in name are fortunately rare which makes the usage of delayed expansion usually no problem. But I want to mention here nevertheless the potential problems which could occur on any folder name containing one or more !.
Something like that should work :
#echo off
set paths[0]="C:\p\test1"
set paths[1]="C:\p\test2"
set paths[2]="C:\p\test3"
SetLocal EnableDelayedExpansion
for /L %%i in (0,1,2) do (
for %%j in (!paths[%%i]!) do echo Processing %%~nxj
)
pause
Define the Array within the function.
This approach can be used to define multiplay Arrays.
#ECHO OFF
Setlocal EnableDelayedExpansion
:: REM P_C is used to define the range of the Array. The -1 operations on P_C is to shift the paths parameter out of the Arrays working Index.
::REM the first parameter passed is used as the Arrays Name. all other parameters are assigned to index values 0 +
Call :process paths "C:\p\test1" "C:\p\test2" "C:\p\test3"
pause
:process
Set P_C=0
Set /a P_C-=1
For %%a in (%*) DO (
CALL :populate %1 "%%~a"
)
Set /a P_C-=1
For /L %%b in (0,1,!P_C!) DO (
ECHO Processing %1[%%b] = "!%1[%%b]!"
)
GOTO :EOF
:populate
Set "%1[!P_C!]=%~2"
Set /a P_C+=1
GOTO :EOF
All;
I've been all around the Internet throughout the day, trying to get a batch file written/working.
Just to start off - I am looking to make this work ONLY via a batch file and/or command line.
I'm looking to remove a specific data (not case-sensitive - as in the data could be 'data' or 'Data' or 'DATA').
Most of the OS's that I've seen the actual data to be on are Win XP machines. The specific's are as follows:
I'm looking to specifically remove the data "browser" (again, it could also be "Browser" or "BROWSER) from a REG_MULTI_SZ registry value, which the subkey is found at:
HKLM\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
The actual value is:
NullSessionPipes
I've always seen multiple pieces of data within this value (with "browser" being somewhere in the mix). Whether browser is the only piece of data in the value, or multiple pieces of data, I need to be able to execute a .bat in order to remove this.
My research points me to querying the data, removing the "browser" data, then re-inserting the remaining - I'm just not able to successfully do this.
I've attempted to just create a dummy Test key/value on my machine (Win10) at the following:
HKCU\Temp\Test
which contains the data of:
Test #1
Test #2
browser
Browser
BROWSER
Test #3
Test #4
Everything I've done, I've been unable to remove only/any form of browser from this data, leaving the rest of the "Test #x" data's.
Please advise on any solution(s) that anyone can come up with.
UPDATE
Here is the code(s) that I've been able to put together throughout my research on this (I have put together several different batch files, without positive results):
(NOTE: I in no way take credit for any of this code, as the majority of it has been compiled from/across many different locations):
Test1.bat
This test kept deleting the entire value, which I could not figure out.
#echo off
for /f "tokens=*" %%a in ('
reg query "HKCU\Temp" /v "Test" /f "browser\0Browser\0BROWSER" /d /e
^| find "REG_MULTI_SZ"
') do (
setlocal enableDelayedExpansion
rem Split
set "line=%%a"
set "value=!line:*REG_MULTI_SZ=REG_MULTI_SZ!"
call set "name=%%line:!value!=%%"
rem Trim spaces
for /L %%b in (1,1,10) do if "!name:~-1!"==" " set "name=!name:~0,-1!"
echo Deleting !name!
reg delete "HKCU\Temp" /v "!name!" /f
endlocal
)
pause
Test2.bat
This was an attempt to export the data into a txt file, then remove "browser" - another failed attempt.
#echo off
reg query HKCU\Temp /v Test > c:\Temp\tmp01.txt
FOR /F "tokens=2,3*" %%a in (c:\Temp\tmp01.txt) do call :sub1 %%b
:sub1
if %1x==browser goto end
echo %1
REG ADD HKCU\Temp /f /v Test /t REG_MULTI_SZ /d %1\0\0\0
:end
goto :eof
Test3.bat
This attempt ended up replacing all data with "%b" for my 'Test' value
#echo off & setlocal ENABLEEXTENSIONS
set k="HKCU\Temp"
set v="Test"
for /f "tokens=*" %%a in ('reg query %k% /v %v%') do (
set "d=%%b"
)
set "d=%d:browser\0=%"
set "d=%d:\0\0=%"
reg add %k% /v %v% /t REG_MULTI_SZ /d "%d%" /f
I did come across some topics where users had stated that if the data was translated to HEX/binary, that this would be easier done?
I hope this helps to give some idea to someone as to what exactly I'm doing wrong here.
As long as none of the lines inside the multi string content are doublequoted, here's how I'd probably write it:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "_k=HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters"
Set "_v=NullSessionPipes"
Set "_s=Browser"
Set "_d="
For /F "EOL=H Tokens=2*" %%A In ('Reg Query "%_k%" /V "%_v%"'
) Do If "%%A"=="REG_MULTI_SZ" Set "_d=%%B"
If "%_d%"=="" Exit /B
Set "_d=%_d:\0\0=%"
Set "_m="
For %%A In ("%_d:\0=","%"
) Do Echo %%A|Find /I "%_s%">Nul||Call Set "_m=%%_m%%%%A\0"
If "%_m%"=="" Exit /B
Reg Add "%_k%" /V "%_v%" /T "REG_MULTI_SZ" /D %_m:~,-2% /F>Nul
Please note that this uses Find, (line 15), to match any line containing Browser, (case insensitive), if you're wanting to match lines containing only the string Browser, (case insensitive), then you may wish to take a look at the FindStr command instead, (enter FindStr /? at the Command Prompt for usage information)
There's one major caveat: the value data for a multi string registry entry can be very long indeed, if you're sure that the string data to be written back to the registry will not exceed maximum character length then you'll be okay. If not I'm afraid I cannot think of any other simple way to perform the task. (writing the data as hex into a registry file, *.reg and import/merging it).
Here is a commented batch file for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "skip=2 tokens=1,2*" %%A in ('%SystemRoot%\System32\reg.exe query HKLM\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters /v NullSessionPipes 2^>nul') do if /I "%%A" == "NullSessionPipes" if "%%B" == "REG_MULTI_SZ" set "NullSessionPipes=%%C" & goto ProcessValue
echo Registry value NullSessionPipes was not found in registry.
goto EndBatch
rem The goal is to remove all ENTIRE strings BROWSER written in any case.
rem Other strings containing by chance also BROWSER at beginning or end
rem of the string should not be removed from multi-string value. For that
rem reason run case-insensitive string substitution with \0browser\0 to
rem really match entire BROWSER string. But there can be multiple BROWSER
rem strings in sequence which are not all removed with one run on using
rem \0browser\0 and so a loop is required make the string substitution
rem with \0browser\0 until this string is not removed anymore from value.
rem But BROWSER could be exist also as first string in value with no
rem preceding \0. For that reason \0 is extra added at beginning and
rem then removed after removing all entire BROWSER strings.
rem REG ADD requires that there is no \0\0 at end of multi-string value
rem to successfully add the multi-string value to Windows registry. The
rem data must end with last character of last string or with just \0.
rem For that reason the last two characters being \0 on Windows XP and
rem Windows Server 2003 are removed before even starting processing the
rem multi-string value. REG of Windows Vista and later Windows versions
rem does not output multi-string value with \0\0 at end like REG of
rem Windows XP. It outputs the multi-string with no \0 at end. So it
rem is necessary to append \0 at end in case of last string is BROWSER.
rem It is also necessary to do nothing if the NullSessionPipes value exists
rem with no string resulting on Windows Vista and later Windows version in
rem nothing assigned to NullSessionPipes and so the environment variable
rem does not exit at all. On Windows XP is assigned in this case just \0
rem which on further processing results also in an empty string and
rem therefore deletion of environment variable NullSessionPipes after
rem removing this final end of multi-string marker.
rem On Windows Vista and later Windows versions it is also necessary to
rem append \0 at end of environment variable string as otherwise with an
rem existing string ending by chance with a backslash this backslash would
rem be interpreted by REG as escape character for the double quote used to
rem enclose the data argument string on command line in double quotes on
rem adding the final multi-line string value. So without appending \0 at
rem end string like TEST\ as last string of NullSessionPipes value would
rem become TEST" which of course is not wanted avoided by using TEST\\0
rem at end of environment variable string for this use case.
rem After removing all entire BROWSER strings from value it is checked
rem if the new value is not identical to value read from registry which
rem means at least one BROWSER string was really removed and so it is
rem necessary to write new value without BROWSER back to Windows registry.
:ProcessValue
if "%NullSessionPipes%" == "" goto EndBatch
if "%NullSessionPipes:~-2%" == "\0" set "NullSessionPipes=%NullSessionPipes:~0,-2%"
if "%NullSessionPipes%" == "" goto EndBatch
if not "%NullSessionPipes:~-2%" == "\0" set "NullSessionPipes=%NullSessionPipes%\0"
set "NewSessionPipes=\0%NullSessionPipes%"
:RemoveBrowser
set "TmpSessionPipes=%NewSessionPipes:\0browser\0=\0%"
if not "%TmpSessionPipes%" == "%NewSessionPipes%" set "NewSessionPipes=%TmpSessionPipes%" & goto RemoveBrowser
set "NewSessionPipes=%TmpSessionPipes:~2%"
if "%NewSessionPipes%" == "%NullSessionPipes%" echo Current NullSessionPipes value does not contain the string BROWSER.& goto EndBatch
echo Current NullSessionPipes value is:
echo/
echo %NullSessionPipes%
echo/
echo New NullSessionPipes value is:
echo/
echo %NewSessionPipes%
echo/
%SystemRoot%\System32\reg.exe add HKLM\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters /v NullSessionPipes /t REG_MULTI_SZ /d "%NewSessionPipes%"
:EndBatch
endlocal
The output of the command line
C:\Windows\System32\reg.exe query HKLM\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters /v NullSessionPipes 2>nul
executed by FOR in a separate command process started with cmd /C in background depends on version of REG.
On Windows Vista and Windows Server 2008 and all later Windows versions the output starts with an empty line, second line is the registry key, and third line contains registry value name, value type and the value data separated by spaces.
On Windows XP and Windows Server 2003 the output starts with an empty line, next version of REG, one more empty line, fourth line contains registry key and fifth line finally contains registry value name, value type and the value data with four indent spaces and separated by a horizontal tab character.
So from different output of REG it is possible only to skip the first two lines. The next line contains the data of interest on Windows Vista/Server 2008 and all later versions of Windows. But on Windows XP and Server 2003 it is necessary to process more lines from captured REG output until fifth line is reached with the data of interest. For that reason the two additional IF conditions are used to be 100% sure that the multi-string value of registry value NullSessionPipes is really assigned to environment variable NullSessionPipes before exiting the loop and processing the value.
Output of REG on Windows XP with NullSessionPipes existing but not containing any string:
! REG.EXE VERSION 3.0
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
····NullSessionPipes» REG_MULTI_SZ» \0
· in output example above and all others below represents an indenting/separating space character. » represents a separating horizontal tab character.
Output of REG on Windows XP with NullSessionPipes existing but not containing any string:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
····NullSessionPipes····REG_MULTI_SZ····
Example output of REG on Windows XP with NullSessionPipes containing strings:
! REG.EXE VERSION 3.0
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
····NullSessionPipes» REG_MULTI_SZ» browser\0test #1\0BROWSER\0Browser\0Test\#2\0TEST\\0browser\0\0
Example output of REG on Windows 7 with NullSessionPipes containing strings:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
····NullSessionPipes····REG_MULTI_SZ····browser\0test #1\0BROWSER\0Browser\0Test\#2\0TEST\\0browser
For the example with multiple strings the data string to add on command line is: "test #1\0Test\#2\0TEST\\0"
The command line with REG to add modified value to registry does not contain option /f to force an overwrite. That gives you the possibility to check new value before really writing it to registry. Insert parameter /f left to /d if there should be no prompt to overwrite existing value once you verified that the batch file works as expected by you.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
goto /?
if /?
reg /?
reg add /?
reg query /?
set /?
setlocal /?
See also Single line with multiple commands using Windows batch file for an explanation of operator &.
May be out of line here but I stumbled on a batch script here on Stack Overflow and it was just what I wanted to do. But it is a quit old post and I can't get it to work.
Original post: Batch to query registry key, find a string within that key, then create a new key
:start
setlocal ENABLEDELAYEDEXPANSION
set qry=reg query "HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}" /s /v DriverDesc
for /f "Tokens=*" %%p in ('%qry%') do (
set var=%%p
set var=!var:^&=!
set var=!var:^)=!
set var=!var:^(=!
call :parse
)
endlocal
goto :EOF
:parse
if /i "%var:~0,5%" NEQ "HKEY_" goto parse1
set key=%var%
set key=%key:HKEY_LOCAL_MACHINE=HKLM%
goto :EOF
:parse1
setlocal ENABLEEXTENSIONS
for /f "Tokens=*" %%f in ('#echo %var%^|findstr /i /c:"Intel(R)"') do (
if defined key reg add %key% /v PnPCapabilities /t REG_DWORD /d 56 /f&set key=
)
endlocal >nul 2>&1
I executed this batch file on Windows 10 Home Edition and it has not changed anything in Windows registry although running it as administrator.
Can someone please help me to get this script to work?
Result of reg query:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0000
DriverDesc REG_SZ Microsoft Kernel Debug Network Adapter
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0001
DriverDesc REG_SZ Intel(R) Ethernet Connection I217-V
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0002
DriverDesc REG_SZ Qualcomm Atheros AR946x Wireless Network Adapter
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0003
DriverDesc REG_SZ Broadcom 802.11ac Network Adapter
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0004
DriverDesc REG_SZ Bluetooth Device (RFCOMM Protocol TDI)
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0005
DriverDesc REG_SZ Microsoft Wi-Fi Direct Virtual Adapter
The batch file written by Matt M does not work because all &, ( and ) are removed from all lines before calling the subroutine parse. For that reason Intel(R) is modified already to IntelR and FINDSTR cannot find in any modified line the string Intel(R). That's why reg add is never executed by batch file posted in question.
I decided to rewrite the batch code for the task to disable power management on all Intel ® network adapters.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "DetectionCount=0"
for /F "delims=" %%I in ('%SystemRoot%\System32\reg.exe query "HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}" /s /v DriverDesc 2^>nul') do call :ProcessLine "%%I"
if not %DetectionCount% == 0 echo/ & pause
endlocal
goto :EOF
:ProcessLine
set "RegistryLine=%~1"
if "%RegistryLine:~0,5%" == "HKEY_" set "RegistryKey=%~1" & goto :EOF
for /F "tokens=2*" %%A in ("%~1") do set "AdapterDescription=%%B"
if "%AdapterDescription:Intel(R)=%" == "%AdapterDescription%" goto :EOF
echo/
%SystemRoot%\System32\reg.exe add "%RegistryKey%" /v PnPCapabilities /t REG_DWORD /d 56 /f >nul
if errorlevel 1 (
echo Failed to add double word value "PnPCapabilities" with value 56 for
) else (
echo Added successfully double word value "PnPCapabilities" with value 56 for
)
echo network adapter "%AdapterDescription%" at registry key:
echo %RegistryKey%
set /A DetectionCount+=1
goto :EOF
How was the batch file tested?
I tested the script with the posted output of registry query
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}" /s /v DriverDesc
copied into text file RegistryOutput.txt and using the FOR loop
for /F "delims=" %%I in (RegistryOutput.txt) do call :ProcessLine "%%I"
instead of the FOR loop in posted batch code processing the output of the registry query directly. The line with reg.exe add was disabled by putting echo at beginning of this line.
The output of the batch file was:
Added successfully double word value "PnPCapabilities" with value 56 for
network adapter "Intel(R) Ethernet Connection I217-V" at registry key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0001
How does the batch file work?
The FOR command executes console application REG in a background command process with the command line:
C:\Windows\System32\cmd.exe /c C:\Windows\System32\reg.exe query "HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}" /s /v DriverDesc 2>nul
REG could output an error message to handle STDERR if it cannot find the specified registry key. This is very unlikely because this is the registry key for network adapters. However, the error message would be redirected to device NUL to suppress it using 2>nul. Please read the Microsoft article about Using Command Redirection Operators for details about input and output redirection. The redirection operator > must be escaped here with caret character ^ to be interpreted as literal character when Windows command interpreter processes the entire FOR command line before executing internal command FOR. Otherwise 2>nul would be interpreted as misplaced redirection of command FOR resulting in a syntax error message by Windows command interpreter instead of running FOR.
The output of REG written to handle STDOUT in background command process is captured by FOR and then processed line by line.
Empty lines are skipped by FOR as well as lines starting with a semicolon which does not occur here because no line ever starts with a semicolon.
All other lines would be split up into substrings (tokens) using space and horizontal tab characters as delimiters for the strings. This split behavior is not wanted here. Therefore "delims=" is used to disable line splitting and get assigned to loop variable I each non empty line.
The line with a registry key or registry value DriverDesc is passed enclosed in double quotes to subroutine ProcessLine as first and only argument. An argument string must be enclosed in double quotes if it contains 1 or more spaces or 1 or more of following characters: &()[]{}^=;!'+,`~|<>
The subroutine ProcessLine assigns first the passed line without the double quotes to environment variable RegistryLine.
A line on which the first 5 characters are case-sensitive equal to string HKEY_ is interpreted as line with a registry key assigned to environment variable RegistryKey and the subroutine is exited with goto :EOF.
Otherwise the registry line with a DriverDesc is processed as string by one more FOR loop.
The option "tokens=2*" results in splitting up the line into the three parts:
DriverDesc
REG_SZ
The string starting with first non whitespace character REG_SZ up to end of line.
The second substring (token) REG_SZ is assigned to loop variable A. This string is of no interest for this task. For that reason loop variable A is not referenced by command line executed by FOR.
The third token being the network adapter description is assigned to next loop variable after specified loop variable A according to ASCII table which is B. That is the reason why loop variables are case-sensitive while environment variables are not.
The description of the driver of the network adapter is assigned to variable AdapterDescription for further processing.
The IF condition compares the adapter description with all occurrences of Intel(R) case-insensitive replaced by an empty string and therefore removed from the description with the unmodified network adapter description. Equal strings means the network adapter description does not contain the string Intel(R) resulting in exiting the subroutine with no further processing. In other words the registry key of this network adapter is ignored by the batch file.
But the double word value PnPCapabilities is added to the registry key containing this description value with the decimal value 56 for a network adapter with Intel(R) in description most likely at beginning of the description.
All other lines of the batch file are for informing the user of the batch file if any Intel ® network adapter was detected at all and if the registry value could be successfully added to registry or if that failed, for example on not running this batch file as administrator required for write access to any key of registry hive HKEY_LOCAL_MACHINE.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
reg /?
reg add /?
reg query /?
set /?
setlocal /?
See also the Microsoft support article Testing for a Specific Error Level in Batch Files. The answer on Single line with multiple commands using Windows batch file explains operator &.
I'm trying to execute a batch file "spoon.bat" from PENTAHO (pdi-ce-7.1.0.0-12), but there is an error.
Part of batch file where the error is found:
if "%SPOON_CONSOLE%"=="1" set PENTAHO_JAVA=C:\Program Files (x86)\Java\jre1.8.0_121\bin\java.exe
if not "%SPOON_CONSOLE%"=="1" set PENTAHO_JAVA=C:\Program Files (x86)\Java\jre1.8.0_121\bin\javaw.exe
set IS64BITJAVA=0
call "%~dp0set-pentaho-env.bat"
But I receive next below error:
The system cannot find the path specified
The error is when I'm trying to assign the path where java.exe or javaw.exe are found to "PENTAHO_JAVA".
I've modified the path with double quote mark, but doesn't work; and also I've modified as:
if "%SPOON_CONSOLE%"=="1" set "PENTAHO_JAVA=C:\<Program Files (x86)>\Java\jre1.8.0_121\bin\java.exe"
Any idea to how declare it to fix it?
Where is the environment variable PENTAHO_JAVA referenced?
It must be referenced with "%PENTAHO_JAVA%" because the string assigned to this environment variable contains characters like a space or &()[]{}^=;!'+,`~. This is explained in help of Windows command interpreter output on running in a command prompt window cmd /? in last paragraph on last help page.
It is of course also possible to define the environment variable with the necessary double quotes already added, i.e. use:
if "%SPOON_CONSOLE%"=="1" set "PENTAHO_JAVA="%ProgramFiles(x86)%\Java\jre1.8.0_121\bin\java.exe""
if not "%SPOON_CONSOLE%"=="1" set "PENTAHO_JAVA="%ProgramFiles(x86)%\Java\jre1.8.0_121\bin\javaw.exe""
set "IS64BITJAVA=0"
call "%~dp0set-pentaho-env.bat"
But this is not recommended. Better would be to use
if "%SPOON_CONSOLE%"=="1" set "PENTAHO_JAVA=%ProgramFiles(x86)%\Java\jre1.8.0_121\bin\java.exe"
if not "%SPOON_CONSOLE%"=="1" set "PENTAHO_JAVA=%ProgramFiles(x86)%\Java\jre1.8.0_121\bin\javaw.exe"
set "IS64BITJAVA=0"
call "%~dp0set-pentaho-env.bat"
and reference environment variable PENTAHO_JAVA enclosed in double quotes where it is necessary to specify its value enclosed in double quotes.
Example:
#echo off
rem Get path of latest installed Java directly from Windows registry.
for /F "skip=1 tokens=1,2*" %%N in ('%SystemRoot%\System32\reg.exe QUERY "HKLM\Software\Microsoft\Windows\CurrentVersion\App Paths\javaws.exe" /v Path 2^>nul') do if /I "%%N" == "Path" set "PENTAHO_JAVA=%%P" & goto JavaPathFound
rem Path of Java not found in registry, search for 32-bit Java in the default
rem program files folders of 64-bit and 32-bit Windows and take first found.
if "%ProgramFiles(x86)%" == "" goto Windows_x86
for /R "%ProgramFiles(x86)%" %%I in (java*.exe) do set "PENTAHO_JAVA=%%~dpI" & goto JavaPathFound
:Windows_x86
for /R "%ProgramFiles%" %%I in (java*.exe) do set "PENTAHO_JAVA=%%~dpI" & goto JavaPathFound
echo Error: Java binary directory not found.
echo/
pause
goto :EOF
:ErrorJavaEXE
echo Error: File %PENTAHO_JAVA% not found.
echo/
pause
goto :EOF
:JavaPathFound
if not "%PENTAHO_JAVA:~-1%" == "\" set "PENTAHO_JAVA=%PENTAHO_JAVA%\"
if "%SPOON_CONSOLE%" == "1" (
set "PENTAHO_JAVA=%PENTAHO_JAVA%java.exe"
) else (
set "PENTAHO_JAVA=%PENTAHO_JAVA%javaw.exe"
)
rem Check existence of Java executable to run.
if not exist "%PENTAHO_JAVA%" goto ErrorJavaEXE
"%PENTAHO_JAVA%" -version
call "%~dp0set-pentaho-env.bat"
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
echo /?
for /?
goto /?
if /?
pause /?
reg /?
reg query /?
rem /?
Read also the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul whereby redirection operator must be escaped in this batch code on FOR command line with caret character ^. And read answer on Single line with multiple commands using Windows batch file for an explanation of & operator.
Generally, don't modify the shell scripts coming with Kettle. You're supposed to set certain environment variables to adapt the scripts to your runtime environment. Look at the top comment section in script set-pentaho-env to learn what's best for your system.
BTW: The current Java 8 security baseline 8u131 was released in April 2017 - you're way behind that. Also, are you aware of the fact that you are using a 32bit JVM with limited RAM support?