Batch file to open a file based on the CPU usage - batch-file

#Echo OFF
SET "Caminho=SomePath"
SET /A "UsoMaximo=95"
SET /A "Intervalo=3"
:LOOP
cls
For /F %%P in ('wmic cpu get loadpercentage ^| FINDSTR "[0-9]"') do (
IF %%P GTR %UsoMaximo% (
start %Caminho%
)
)
Ping -n %Intervalo% Localhost >NUL
GOTO :LOOP
I made this code based (almost cloned) on the code in this question: Batch file to restart a specific service based on the CPU of a process.
Basically, it will open a file located in "Caminho" when the CPU usage is higher than 95%.
The problem is i don't want it to open when the CPU usage is higher than 95%, i want it to open when the CPU usage is LOWER than 95%.
How can i do this? What i need to change?

Change GTR to LSS.
IF %%P GTR %UsoMaximo% (
From if /?:
Performs conditional processing in batch programs.
IF [NOT] ERRORLEVEL number command
IF [NOT] string1==string2 command
IF [NOT] EXIST filename command
NOT Specifies that Windows should carry out
the command only if the condition is false.
ERRORLEVEL number Specifies a true condition if the last program run
returned an exit code equal to or greater than the number
specified.
string1==string2 Specifies a true condition if the specified text strings
match.
EXIST filename Specifies a true condition if the specified filename
exists.
command Specifies the command to carry out if the condition is
met. Command can be followed by ELSE command which
will execute the command after the ELSE keyword if the
specified condition is FALSE
The ELSE clause must occur on the same line as the command after the IF. For
example:
IF EXIST filename. (
del filename.
) ELSE (
echo filename. missing.
)
The following would NOT work because the del command needs to be terminated
by a newline:
IF EXIST filename. del filename. ELSE echo filename. missing
Nor would the following work, since the ELSE command must be on the same line
as the end of the IF command:
IF EXIST filename. del filename.
ELSE echo filename. missing
The following would work if you want it all on one line:
IF EXIST filename. (del filename.) ELSE echo filename. missing
If Command Extensions are enabled IF changes as follows:
IF [/I] string1 compare-op string2 command
IF CMDEXTVERSION number command
IF DEFINED variable command
where compare-op may be one of:
EQU - equal
NEQ - not equal
LSS - less than
LEQ - less than or equal
GTR - greater than
GEQ - greater than or equal
and the /I switch, if specified, says to do case insensitive string
compares. The /I switch can also be used on the string1==string2 form
of IF. These comparisons are generic, in that if both string1 and
string2 are both comprised of all numeric digits, then the strings are
converted to numbers and a numeric comparison is performed.
The CMDEXTVERSION conditional works just like ERRORLEVEL, except it is
comparing against an internal version number associated with the Command
Extensions. The first version is 1. It will be incremented by one when
significant enhancements are added to the Command Extensions.
CMDEXTVERSION conditional is never true when Command Extensions are
disabled.
The DEFINED conditional works just like EXIST except it takes an
environment variable name and returns true if the environment variable
is defined.
%ERRORLEVEL% will expand into a string representation of
the current value of ERRORLEVEL, provided that there is not already
an environment variable with the name ERRORLEVEL, in which case you
will get its value instead. After running a program, the following
illustrates ERRORLEVEL use:
goto answer%ERRORLEVEL%
:answer0
echo Program had return code 0
:answer1
echo Program had return code 1
You can also use numerical comparisons above:
IF %ERRORLEVEL% LEQ 1 goto okay
%CMDCMDLINE% will expand into the original command line passed to
CMD.EXE prior to any processing by CMD.EXE, provided that there is not
already an environment variable with the name CMDCMDLINE, in which case
you will get its value instead.
%CMDEXTVERSION% will expand into a string representation of the
current value of CMDEXTVERSION, provided that there is not already
an environment variable with the name CMDEXTVERSION, in which case you
will get its value instead.

Related

Multiple testing in batch file - REG QUERY

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 /?

Batch Script that checks a directory for duplicate files

Trying to look through a file for example,
C:\Users\Admin\Desktop\Test\12345
C:\Users\Admin\Desktop\Test\45635
C:\Users\Admin\Desktop\Test\12345-2018-04-21
and create a subfolder named "12345" and will store the two files (12345 and 12345-2018-04-21) in subfolder "12345". and this will loop through the whole folder to make sure there is no copies
:: ---------------------------------------------------------------
:: - This is a program that searches for files with the same 5
:: digit number and puts them in a subfolder.
:: ---------------------------------------------------------------
#ECHO off
SETLOCAL
SET "sourcedir=C:\Users\Admin\Desktop\Test"
SET "destdir=C:\Users\Admin\Desktop\Test"
SET /a lastnum=200000
if "%first5%" equ "%lastnum%"(
SET "destdir=%destdir%\%first5%-sub"
md %destdir%
)
FOR /f "delims=" %%a IN (
'dir /b /a-d /on "%sourcedir%\*" '
) DO (
CALL :detect "%%a"
IF DEFINED dupnum (ECHO(MOVE "%sourcedir%\%%a" "%destdir%\")
)
GOTO end
:: Routine to detect whether the first 5 characters of the filename "%1"
:: are all numeric and if so, whether they match the previous 5-digit number
:: found.
:detect
SET "dupnum="
:: Get the first 5 characters of the filename; prefix with a `1`
SET "fullfilename=%~1"
SET "first5=1%fullfilename:~0,5%"
IF "%first5%" neq "%lastnum%" (
SET "lastnum=%first5%"
GOTO end
)
:: First 5 chars of this filename = first 5 of previous filename
:: Check to see whether numeric
SET /a dummy=first5 + 0
IF "%dummy%" neq "%first5%" (
SET "lastnum=%first5%"
GOTO end
)
SET "dupnum=Y"
GOTO end
ENDLOCAL
:end
PAUSE
Not sure why i can not get it to make a folder and store the duplicated files into it. It will read through my test folder and notice the duplicates and prompt to move them but i am not sure what i need to do to move it to a new folder.
It's difficult to determine where to start if you've not encountered the language before.
The first issue is to properly specify what your terms mean. A "duplicate file" for instance would normally mean duplicate contents or a duplicate name (but since a directory can only hold one file with any particular name, this would imply different directories). In your case, your individual definition is "files whose names start with the same 5 digits".
Since this leads to a batch which uses many techniques (and there can be many approaches) then this is one that could be used:
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET /a lastnum=200000
md %destdir% 2>nul
FOR /f "delims=" %%a IN (
'dir /b /a-d /on "%sourcedir%\*" '
) DO (
CALL :detect "%%a"
IF DEFINED dupnum (ECHO(MOVE "%sourcedir%\%%a" "%destdir%\")
)
GOTO :EOF
:: Routine to detect whether the first 5 characters of the filename "%1"
:: are all numeric and if so, whether they match the previous 5-digit number
:: found.
:detect
SET "dupnum="
:: Get the first 5 characters of the filename; prefix with a `1`
SET "fullfilename=%~1"
SET "first5=1%fullfilename:~0,5%"
IF "%first5%" neq "%lastnum%" (
SET "lastnum=%first5%"
GOTO :eof
)
:: First 5 chars of this filename = first 5 of previous filename
:: Check to see whether numeric
SET /a dummy=first5 + 0
IF "%dummy%" neq "%first5%" (
SET "lastnum=%first5%"
GOTO :eof
)
SET "dupnum=Y"
GOTO :eof
You would need to change the settings of sourcedir and destdir to suit your circumstances.
The required MOVE commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MOVE to MOVE to actually move the files. Append >nul to suppress report messages (eg. 1 file moved)
Fundamentally, batch code is case-insensitive with the notable exception of the metavariable in a for statement (%%a above)
Batch is however quite layout-sensitive, so best to cut-and-paste rather than retyping and attempting to re-format to suite some more-pleasing-to-the-eye style.
It's normally assumed that batch code is run from the prompt, not by 'clicking'.
The syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned. set /a can normally be used "quoteless".
The #echo off statement turns command-echoing off so that the command is not echoed to the console before being executed.
The setlocal statement invokes a 'local environment' so that any alterations or additions to the environment are disposed of when the batch finishes.
The main loop first executes a dir command to produce a directory list. The options chosen are /b - basic (no header/footer - names only) /a-d - no directorynames and /on - order-by-name (ie alphabetical)
The output of the dir command is then processed by a for /f with the "delims=" option. This assigns the entire line to the metavariable %%a.
For each filename found, the subroutine :detect is executed. Since that rouine is located within this batch file, a : is used in the call statement. Without the :, the routine called would be a batchfile named detect (either detect.bat or detect.cmd, the former being more popular)
After the routine :detect has been executed, a variable named dupnum may or may not be set. If it's set, echo the string. If not, don't.
The routine :detect first "set"s dupnum (the result of the routine) to an empty string, which removes the variable from the environment if it exists.
The variable fullfilename is then set to the value passed from the call statement. %1 means "first parameter". %~1 means "and remove enclosing quotes". The quotes are required to allow the filename being passed to contain spaces.
We then get the first 5 characters from the full filename (from "character 0" for 5 characters) and prefix this with a 1. Batch regards a string which starts with a 0 as an octal string if used in a mathematical operation, so the 1 prefix ensures the now 6 characters are interpreted as decimal.
Now - if the first5 is not equal to the lastnum, then we record the new lastnum and goto :eof which exits the subroutine. dupnum has not be set, so the calling loop will not show this filename.
Next, we only want to do the move if the first 5 (well, now 6) chars are all numeric. We can add 0 to the string in mathematical mode using a set /a which will either result in the same number (if these chars are all-numeric) or with a different number (up to the first non-numeric) if the "first5" contains a non-numeric.
So - test the result - if different, "first5" contains a non-numeric, so bail out with dupnum not set. If the same, then set dupnum so the filename is reported.
Please also note that the official remark instruction is rem, but :: (which is a broken label - one which can't be reached) is often used instead for remarks as it is easier to type and less intrusive to the eye. Note however that the :: style can't be used within code-blocks (statements grouped between parentheses)
I've added a new line, md ... which should create the destination directory. The 2>nul suppresses error messages should that directory already exist. If you want this directory to be a subdirectory of the source directory, then set destdir thus:
set "destdir=%sourcedir%\subdirectorynamedesired"

Batch - Remove Range/specific words

In perforce I print the list of shelved/changes to the file with the following batch script :
for %%A IN (%ShelvedCHL%) DO (
echo Change List: %%A
p4 -p %PPort% -c %PClient% unshelve -s %%A && if NOT %ERRORLEVEL%==0 exit -1
)>> list.txt
Here is my list.txt
Change List: 24536
//GSA/TOC.h#1 - unshelved, opened for edit
... /GSA/TOC.h - also opened by test#dev
... /GSA/TOC.h - also opened by test#dev
//odata/oenums.h#6 - unshelved, opened for edit
... //odata/oenums.h#6 - also opened by test#dev
I want to have the following output, basically remove sentence after - with dash as well : or even any perforce command to have less information and just list of files :
//GSA/TOC.h#1
... /GSA/TOC.h
... /GSA/TOC.h
//odata/oenums.h#6
... //odata/oenums.h#6
I would appreciate any help, thanks in advance!
You can do this in p4 natively without doing hardly any scripting at all. Check out this blog post:
https://www.perforce.com/blog/fun-formatting
You can see the fields in a Perforce message by running it with the -e global opt*, like this:
p4 -e files ...
Then you can use any or all of those fields when reformatting the output, like this:
p4 -F "just the filename please: %depotFile%" files ...
*see also -Ztag which gives you an alternate dictionary of output fields
At first, let us take a look at your code:
I do not know the program p4, but I assume it is setting the ErrorLevel. So since this value is updated in the same block of code as you want to read it, you need to use delayed expansion, so place setlocal EnableDelayedExpansion on top of your script and use !ErrorLevel! instead of %ErrorLevel%. Another way is to replace if not !ErrorLevel!==0 by ifnot ErrorLevel 1, meaning if ErrorLevel is not greater than and not equal to 1, or expressed in a simpler way, if ErrorLevel is less than 1, but this works only if the program does not set a negative value.
Even if you corrected the ErrorLevel issue, the if query would never eb executed because of the conditional command concatenation operator %%, because this lets the following command only execute in case the preceding one succeeded, meaning that its exit code1 equals zero. Therefore to execute the if statement, use the unconditional operator &. Anyway, there is also another conditional operator ||, which lets the following command only execute in case the exit code is a non-zero value; this one could replace your if condition completely.
The exit command does not only quit the batch file, it also terminates the command prompt (cmd) instance which the batch script ran in. To quit the batch file only use exit /B instead.
You are setting the ErrorLevel to -1 by exit -1. You can do this, of course, but usually negative values are avoided; hence let me suggest a positive value like 1 (by exit /B 1).
You are opening and closing the file list.txt for every single iteration of the for loop. This reduces overall performance. Furthermore, if list.txt already exists, the data becomes appended; if you do not want that you need to place del "list.txt" 2> nul before the for loop to initially delete the file. Anyway, to write the entire file at once, put another pair of parentheses around the for loop. You can then chose whether to append to an already existing file using the redirection operator >>, or to overwrite it using operator > (without any need to delete it first).
All this results in the following improved script:
(for %%A in (%ShelvedCHL%) do (
echo Change List: %%A
p4 -p %PPort% -c %PClient% unshelve -s %%A || exit /B 1
)) > "list.txt"
Depending on what %ShelvedCHL% contains (it seems to be 24536 in your sample data, so not a file path/name/mask), the for loop might even be superfluous, although I cannot know at this point...
Anyway, all of the above does still not yet account for removal of the partial string beginning with SPACE + - + SPACE, so let us implement this now:
To keep it simple, we could just modify the file list.txt after the above code, using this code (see all the explanatory rem remarks; the mentioned string manipulation is called sub-string substitution):
rem // Read file `list.txt` line by line:
(for /F "usebackq delims= eol=|" %%L in ("list.txt") do (
rem // Assign line string to variable:
set "LINE=%%L"
rem // Enable delayed expansion to be able to do string manipulation:
setlocal EnableDelayedExpansion
rem /* Replace every occurrence of ` - ` by a single character `|`, then use this one
rem as a delimiter to split the line string as `for /F` requires single-character
rem delimiters; just using `-` is not good as they might occur in the partial
rem strings that need to be kept, I suppose; the `|` must not occur in them: */
for /F "tokens=1 delims=| eol=|" %%K in ("!LINE: - =|!") do (
rem // Disable delayed expansion to not lose `!`-marks:
endlocal
rem // Return the split string, that is the part before the (first) ` - `:
echo %%K
)
)) > "list_NEW.txt"
The resulting data is contained in the file list_NEW.txt. To have it in the original file, append the following line to the code:
move /Y "list_NEW.txt" "list.txt" > nul
1... Usually the exit code and ErrorLevel are the same, but there are in fact some rare cases where they may differ.

Why does Windows command interpreter output the error message: ) was unexpected

I'm making a Minecraft modding tool using a batch file. But on execution of the batch file the Windows command interpreter outputs the syntax error message:
) was unexpected
I can't figure out why.
This is my code:
#echo off
cd mods
setlocal enabledelayedexpansion
set "selected=1"
call:print 1
call:print 2
:menu
choice /c wse>nul
if "%errorlevel%"=="2" (
if not !selected! GEQ !a! (
set /a "selected+=1"
cls
call:print 1
call:print 2
)
)
if "%errorlevel%"=="1" (
if not !selected!==1 (
set /a "selected-=1"
cls
call:print 1
call:print 2
)
)
if "%errorlevel%"=="3" (
)
goto menu
:print
if "%1"=="1"set a=0
echo.
if "%1"=="1" (
echo Uninstalled:
) else (
echo Installed:
)
echo.
for %%f in (*.jar) do (
if "%1"=="1" (
if NOT EXIST
"C:/Users/Coornhert/AppData/Roaming/.minecraft/mods/%%~nf.jar" (
set /a "a+=1"
if "!a!"=="!selected!" (
echo -%%~nf
) else (
echo %%~nf
)
set "b=!a!"
)
) else (
if EXIST "C:/Users/Coornhert/AppData/Roaming/.minecraft/mods/%%~nf.jar" (
set /a "a+=1"
if "!a!"=="!selected!" (
echo -%%~nf
) else (
echo %%~nf
)
set "b=!a!"
)
)
)
goto :eof
And it works, but when I hit s, execution terminates with the error message.
Folder structure of folder containing the batch file:
mods
Foo.jar
Foo2.jar
Folder structure of target folder:
C:\Users\Coornhert\AppData\Roaming\.minecraft\mods
Foo.jar
I partly do not understand what this batch file should do, but here is the batch file rewritten with several improvements.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
rem cd /D "%~dp0mods"
pushd "%~dp0mods"
set "a=0"
set "selected=1"
call :PrintIt 1
call :PrintIt 2
:Menu
choice /C wse /N
if errorlevel 3 popd & endlocal & goto :EOF
if errorlevel 2 goto AddOne
if %selected% == 1 goto Menu
set /A selected-=1
cls
call :PrintIt 1
call :PrintIt 2
goto Menu
:AddOne
if %selected% GEQ %a% goto Menu
set /A selected+=1
cls
call :PrintIt 1
call :PrintIt 2
goto Menu
:PrintIt
if %1 == 1 set "a=0"
echo/
if %1 == 1 (echo Uninstalled:) else echo Installed:
echo/
for %%I in (*.jar) do (
if %1 == 1 (
if not exist "%APPDATA%\.minecraft\mods\%%~nI.jar" (
set /A a+=1
if !a! == %selected% (echo -%%~nI) else echo %%~nI
set "b=!a!"
)
) else (
if exist "%APPDATA%\.minecraft\mods\%%~nI.jar" (
set /A a+=1
if !a! == %selected% (echo -%%~nI) else echo %%~nI
set "b=!a!"
)
)
)
goto :EOF
It does nothing useful as is, but batch code in question is also not useful at all.
The applied improvements are:
The command SETLOCAL is moved to top of file. The reason is:
It pushes path of current directory on stack.
It pushes state of command extensions on stack.
It pushes state of delayed expansion on stack.
It pushes the memory address of the current environment variables table on stack.
It creates a copy of the current environment variables table in memory and makes this new environment variables table active.
It sets command extensions and delayed expansion according to the specified parameters if the command is called with parameters at all.
The command ENDLOCAL is executed before leaving batch file. The reason is:
It deletes the current environment table which means no environment variable defined in this batch file exists anymore after ENDLOCAL except it existed already before execution of command SETLOCAL.
It pops memory address of previous environment table from stack and uses this address resulting in restoring initial environment variables.
It pops state of delayed expansion from stack and disables/enables delayed expansion accordingly.
It pops state of command extensions from stack and disables/enables command extensions accordingly.
It pops previous current directory path from stack and sets current directory to this path to restore the current directory.
So the entire command process environment is restored on exit of this batch file to exactly the same environment as it was on starting the batch file.
This makes it possible to call this batch file from within another batch file or from within a command prompt window with no impact on calling batch file or command process.
The command CD could be extended to include drive and path of argument 0 which is the full path of the batch file ending with a backslash because the subdirectory mods is most likely always expected in directory of the batch file and it should not matter what is the current directory on running the batch file.
But cd /D "%~dp0mods" could fail if the batch file is located on a network share accessed using UNC path and therefore command PUSHD is used instead working with enabled command extensions also for UNC paths.
In all programming and scripting languages it is required that variables are defined and initialized with a value before being used the first time. For that reason the environment variables a and selected are defined at top of the batch file with default values. By the way: a is a very bad name for a variable. Why? Search for a in batch file. It is quite often found on not using special find features like whole word only, isn't it.
PRINT is a command as it can be seen on running in a command prompt window print /?. While it is possible to use command names as labels or as names for subroutines, it is not advisable to do so as it could be confusing for readers of the batch file.
The command CHOICE has the option /N to hide the list of choices in the prompt. It is better to use this option than redirecting the output of CHOICE to device NUL.
The very old but still valid Microsoft support article Testing for a Specific Error Level in Batch Files explains that if errorlevel X means that the condition is true if the exit code of previous command or application is greater or equal X. The command CHOICE with 3 choices exits always with 1, 2 or 3 as exit code. So it is best to use:
if errorlevel 3 rem Do something on third choice avoiding fall through to next line.
if errorlevel 2 rem Do something on second choice avoiding fall through to next line.
Do something on first choice.
The advantage of using this method is that it even works with CHOICE within a command block on which if %ERRORLEVEL% == X fails because of delayed expansion would be required and usage of if !ERRORLEVEL! == X.
The integer comparison if %selected% GEQ %a% would not work if the two arguments would be enclosed in double quotes as the double quotes are also interpreted as part of the arguments to compare. For that reason using if "%selected%" GEQ "%a%" would result in running a string comparison instead of an integer comparison. For more information about comparing values with IF look on answer on Exit a for loop in batch.
It is safe here to omit the double quotes also on the other IF conditions with == operator running string comparisons because the environment variables selected and a must be both defined before running this IF condition and therefore both variables are defined at top of the batch file.
The answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? explains why set "variable=value" should be always used to assign a value to an environment variable or delete an environment variable on omitting the value. And this answer also explains why on set /A variable=expression the double quotes can be usually omitted as whitespace characters are interpreted completely different within an arithmetic expression. The exception is usage of set /A with 1 or more commands on same command line on which double quotes around variable=expression would be also needed.
The batch file should be exited when the batch file user enters e or E to take third choice. This could be done with just goto :EOF, or with exit /B which is an alias for goto :EOF, or with just exit which always exits entire command process independent on calling hierarchy which is not recommended. Windows command interpreter would implicitly restore the initial stack before finishing batch file execution. But it is nevertheless good coding practice to pop from stack with code which was pushed on stack before with code. For that reason there is used popd & endlocal & goto :EOF. See answer on Single line with multiple commands using Windows batch file for more information about usage of multiple commands on one command line.
The list of predefined environment variables of used user account is output on running in a command prompt window the command set. One predefined Windows environment variable is APPDATA with path to application data of current user account. This environment variable should be used instead of a fixed path to application data directory of user account.
And the directory separator on Windows is the backslash character \ and not slash character / as on Unix and Mac.
The usage of f as loop variable is not recommended as this is also a loop variable modifier. %%~f can be interpreted by Windows command interpreter as value of loop variable f without surrounding double quotes or as incomplete loop variable reference because of missing loop variable after %%~f which could be also interpreted as full file name of ?. So it is better to use # or $ as loop variable or upper case letters to avoid such a confusion on interpreting the loop variable reference. Loop variables are case-sensitive.
I prefer for IF conditions with an ELSE branch the coding style
if condition (
command
) else (
command
)
But here in this batch file with command being just a short ECHO command the code is better readable on being more compact with using:
if condition (echo short message) else echo other short message
Delayed expansion for an environment variable referenced within a command block started with ( and ending with matching ) is only needed if the environment variable is also modified in same command block. Therefore environment variable a must be referenced in body of FOR with usage of delayed expansion while environment variable selected can be referenced as usual because of not modified within this command block at all.
It is better to use echo/ to output an empty line instead of echo.. For the reason read the DosTips forum topic: ECHO. FAILS to give text or blank line - Instead use ECHO/
For a basic understanding of the used commands, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
choice /?
cls /?
echo /?
endlocal /?
for /?
goto /?
if /?
popd /?
pushd /?
rem /?
set /?
setlocal /?

Batch File to Search for a String within Directory Names

My batch file terminates prematurely after I assign the first environmental variable (script output below). I've tried turning echo on, using errorlevels, sending the output to a text file, and checking syntax. I've spent several hours researching debugging batch scripts, but I have finally hit a brick wall.
Script's Goal: Search each directory name of the user's Program Files, looking for common antivirus programs. I realize that it would be easiest iterate through an array of antivirus names for this purpose, but I want to keep it simple for now.
#echo off
::variables
set AntiVirus1="Initial Value"
IF NOT ERRORLEVEL 0 echo %ERRORLEVEL%
else echo "env. variable created successfully."
for /d %%f in (""%ProgramFiles%\*"") do (
{
IF NOT ERRORLEVEL 0 echo %ERRORLEVEL%
echo "%%f"
if exist /i "*McAfee*" < %%f %AntiVirus1%="McAfee"
::find "Norton" < %%f
::find "Comodo" < %%f
::find "AVG" < %%f
}
echo %AntiVirus1%
#pause
Output of this script:
C:\Users\Matt\Desktop>set AntiVirus1="Initial Value"
C:\Users\Matt\Desktop>
Can someone point me to what I'm doing wrong?
UPDATE Corrected script, now working but returning incorrect results:
::#echo off
::variables
set AntiVirus1="Initial Value"
IF NOT ERRORLEVEL 0 (echo %ERRORLEVEL%) ELSE echo "env. variable created successfully."
echo Checking Program Files...
for /d %%f in ("%ProgramFiles%\*") do (
echo "%%f"
if %%f=="*adobe*" set AntiVirus1="adobe"
)
echo %AntiVirus1% found
#pause
First of all, ELSE must be on the same line with IF or on the same line with the closing parenthesis that pertains to IF. In you particular case you should change your first IF...ELSE command like this:
IF NOT ERRORLEVEL 0 (ECHO %ERRORLEVEL%) ELSE ECHO "env. variable created successfully."
or like this:
IF NOT ERRORLEVEL 0 (
ECHO %ERRORLEVEL%
) ELSE ECHO "env. variable created successfully."
(Capitalisation and indentation are perfectly optional.)
Other issues:
Duplicated quotation marks in the FOR loop header:
for /d %%f in (""%ProgramFiles%\*"") do (
should be
for /d %%f in ("%ProgramFiles%\*") do (
Braces ({, }) around the loop body. They are not part of the loop syntax (in fact, they are not part of batch scripting syntax at all), so should be dropped.
No closing parenthesis matching the opening one after DO. It should be added on a separate line after the loop body.
Incorrect use of ::-style comments in the loop body. They are not allowed inside bracketed blocks. Use REM instead.
UPDATE
In batch scripting, testing for a substring is done somewhat unusually. You'll need another environment variable and you'll also need to enable delayed expansion. The latter is not really connected with the comparison, but it is needed because the comparison is going to be performed within a bracketed block.
Here's your new script modified, with the changes highlighted:
::#echo off
::variables
set AntiVirus1="Initial Value"
IF NOT ERRORLEVEL 0 (echo %ERRORLEVEL%) ELSE echo "env. variable created successfully."
SETLOCAL EnableDelayedExpansion
echo Checking Program Files...
for /d %%f in ("%ProgramFiles%\*") do (
echo "%%f"
SET "folder=%%f"
if /I NOT "!folder:adobe=!"=="!folder!" set AntiVirus1="adobe"
)
echo %AntiVirus1% found
#pause
Here's a bit of explanation.
The ! syntax is a delayed expansion equivalent of % and is used with environment variables only, not with loop variables and not with command line parameters. Delayed expansion is needed because we are in a bracketed block. A bracketed block is parsed entirely before it starts executing, so all %var% expressions are expanded (evaluated) before the block starts and are not changed throughout the block's execution. That cannot suit us because we need to assign different values to a variable during the block's execution, and the values must be read within the block. Delayed expansion, as follows from the name, delays the expansion of a variable until the actual execution of every single command that references that variable. Because immediate expansion can still be used alongside delayed expansion, a different syntax is introduced, which is ! around variable names, instead of %.
!folder:adobe=! means evaluate folder replacing every occurrence of adobe with an empty string. The result of this expression is then compared to the (unchanged) value of folder. If there's a match, then the replacement didn't occur, which means there was no adobe in the value of folder in the first place. In this case we should do nothing. But if there was not a match, i.e. if the modified value didn't match the unmodified one, then we should set the AntiVirus1 variable. This is why there's NOT in front of the comparison.
The /I option simply means case-insensitive comparison.

Resources