Batch file - get and parse CMD output string - batch-file

I never wrote a batch file, and now I have to write a batch file that runs a command line and parse its output. (e.g: CMD: Diskpart List volume, Output: list of volumes and the free space, I want to find the volume with the maximum free space)
My questions:
what should I write to get the output?
How can I parse it?
Thanks you all,

DiskPart list volumes will not give you free space, but I assume that was only an example.
This batch will give you drive with maximum capacity on your system.
It uses enough constructs to get you going with your own requirements...
You will need to put the following into a batch file
#echo off
echo ****************************************************
echo *** Give maximum drive size on a given system ****
echo ****************************************************
echo *** This script shows various batch constructs. ****
echo *** Use at your own risk - no warranty given ****
echo *** that's fit for any intended purpose :-)) ****
echo *** or that it's error free ****
echo ****************************************************
echo.
REM All of our vars will be local - we do not want to pollute environment. For more info exec 'help setlocal' from cmdline
REM implied endlocal will be issued at the end, no need to insert it
setlocal
REM name of temp file we will use
set tmpFile=tmp.txt
REM power will store multiplier we are currently working in
set power=0
REM maximum found drive size
set maxSize=0
REM Enable delayed expansion - must be on, otherwise all vars will be expanded on input. For more info exec 'help setlocal' from cmdline
setlocal enabledelayedexpansion
REM enable extensions (on by default). Please see help setlocal
setlocal enableextensions
REM get input from user. For more info exec 'help set' from cmdline
set /P cmd=Give diskpart command you want to run [list volume]
REM set default command if user did not set anything
if NOT DEFINED cmd set cmd=list volume
REM set file to contain command we want to run
echo %cmd%>%tmpFile%
REM Skip 8 first lines, then read each line as token. For more info exec 'help for' from cmdline
REM use of backquote will enable us to use temp file with spaces in name
set ERRORLEVEL=0
for /f "tokens=* skip=8 usebackq" %%i in (`diskpart /s %tmpFile%`) do (
set line=%%i
REM read 5 chars from input token, starting at char 49, convert it to number and assign to env var. For more info exec 'help set' from cmdline
REM This also shows delayed expansion convention (!var!).
set /A tsize=!line:~49,5!*1
REM test for unequality (we want to run body only if given size is not 0). For more info exec 'help if' from cmdline
REM see also other operators used (LSS, GEQ
if !tsize! NEQ 0 (
REM it's not possible to do most obvious (multiplication) as this would overflow.
REM '(' is block character. Look how they are positioned, it has to be this way!
REM Mind also where you can put spaces!
REM 'set a=7' is different to 'set a=7 '
set unit=!line:~54,2!
REM Mind use of '
if '!unit!'==' B' (
if !power! LSS 1 (
set power=1
set maxSize=!tSize!
)
if !power!==1 (
if !tsize! GEQ !maxsize! (
set maxSize=!tsize!
set maxUnit=!unit!
set maxDrive=!line!
)
)
)
if !unit!==KB (
if !power! LSS 3 (
set power=3
set maxSize=!tSize!
)
if !power!==3 (
if !tsize! GEQ !maxsize! (
set maxSize=!tsize!
set maxUnit=!unit!
set maxDrive=!line!
)
)
)
if !unit!==MB (
if !power! LSS 6 (
set power=6
set maxSize=!tSize!
)
if !power!==6 (
if !tsize! GEQ !maxsize! (
set maxSize=!tsize!
set maxUnit=!unit!
set maxDrive=!line!
)
)
)
if !unit!==GB (
if !power! LSS 9 (
set power=9
set maxSize=!tSize!
)
if !power!==9 (
if !tsize! GEQ !maxsize! (
set maxSize=!tsize!
set maxUnit=!unit!
set maxDrive=!line!
)
)
)
)
)
REM cleanup
del %tmpFile% /Q
REM this prints empty line
echo.
echo Your max drive is: !maxSize! !maxUnit!
echo.
echo Drive details:
echo !maxDrive!

Here's the easy beginning bit.
echo list volume > %temp%\temp12345
diskpart /s %temp%\temp12345 > %temp%\diskpart.txt
Doing this in batch is a nightmare, do you have any other options?
Edit: To do it in batch, you need to use FOR.
Check For /? for examples on how to parse data.

Related

Batch - Multiple file outputting with variable replace value

I'm trying to create a .bat program to replace two strings inside one text file and output the modified text multiple times.
So far so good...
The purpose of my program is to calculate the number of months between two dates (Ex: 01/2016 and 05/2017 will result in 17 months), and generate one configuration file for each month for a 3rd party program (17 output files in my example). This can be accomplished by replacing two tags ( and ) inside a template configuration file with the respective month/year values in that range.
My code so far is below:
#echo off &setlocal
setlocal ENABLEDELAYEDEXPANSION
cls
set "CNST_SEARCH_YEAR=<VAR_YEAR>"
set "CNST_SEARCH_MONTH=<VAR_MONTH>"
set "CNST_FILE_TEMPLATE=config_template.properties"
set "CNST_FILE_TMP=tmp_config.properties"
rem ===============================
rem INPUT DO USUÁRIO
rem ===============================
set "start_year=2014"
set "start_month=3"
set "end_year=2015"
set "end_month=7"
rem ===============================
rem DEFINIÇÂO DO TOTAL DE ITERAÇÕES
rem ===============================
set /a "iterations=(%end_year%*12 + %end_month%) - (%start_year%*12 + %start_month%) + 1"
echo DISPARO AUTOMATICO DA ROTINA AGENT - v1.0
echo ================================
echo Total de iteracoes: %iterations%
echo ================================
rem ===============================
rem EXECUÇÃO DO LOOP PRINCIPAL
rem ===============================
set v_year=%start_year%
set v_month=%start_month%
for /L %%i IN (1, 1, %iterations%) do (
echo ================================
echo Iteracao: %%i
echo !v_year! / !v_month!
echo Gerando parametrizacoes...
for /f "delims=" %%j in (%CNST_FILE_TEMPLATE%) do (
set "line=%%j"
set "line=!line:%CNST_SEARCH_YEAR%=!v_year!"
set "line=!line:%CNST_SEARCH_MONTH%=!v_month!"
echo !line! >> "%CNST_FILE_TMP%_%%i"
)
echo Executando Agent...
rem jre\bin\java.exe -jar gdc-agent-totvs-2.0.0.jar %CNST_FILE_TMP%
echo Apagando arquivo temporario...
rem del %CNST_FILE_TMP%
IF !v_month! EQU 12 (
set v_month=1
set /a v_year=!v_year!+1
) ELSE (
set /a v_month=!v_month!+1
)
echo ================================
)
endlocal
pause
My problem relies in the lines:
set "line=!line:%CNST_SEARCH_YEAR%=v_year!"
set "line=!line:%CNST_SEARCH_MONTH%=v_month!"
Because I can't use delayedExpansion multiple times inside that command. Also I can't define the v_year and v_month variables before the for loop, because their values are being set by the loop.
I'm using plain batch script since this program will be sent to other people who might not have powershell or other scripting tool.
Any ideas people?
Thanks.
Or combine the old fashioned call variant
call set "line=%%line:!CNST_SEARCH_YEAR!=!v_year!%%"
call set "line=%%line:!CNST_SEARCH_MONTH!=!v_month!%%"
To escape a percent sign from being interpreted as enclosing a variable you have to double it. The parser reduces the two %% to a single one in this step.
The normal delayed expansion for the !var! is executed.
The call forces a second evaluation of the parser which find this time the single percent signs and acts on current values.
To learn more on this topic read How does the Windows Command Interpreter (CMD.EXE) parse scripts?
You can try with something like
for %%v in (!v_year!) do set "line=!line:%CNST_SEARCH_YEAR%=%%v!"
This simply moves the delayed expanded value into a for replaceable parameter that can be used in the delayed expansion expression used in the set command

Missing Operator when using set /a

So I am creating a Batch Operating System, but the problem is... IT KEEPS SAYING MISSING OPERATOR!!
Here is a snippet of my code:
#echo off
title LiME - Version 1.0
mode con: cols=86 lines=21
if exist "\SystemLiME" goto startup.jsc
) else (
if not exist "\SystemLiME" goto boot.jfk
:boot.jfk
cls
echo MAINBOOTCONFIG
echo.
echo CHECKING FOR VOLUMES TO CREATE..
ping localhost -n 5 >nul
if exist "\SystemLiME" set create-vol1=0
) else (
if not exist "\SystemLiME" set create-vol1=1
if exist "\SystemLiME\pkgs" set create-vol2=0
) else (
if not exist "SystemLiME\pkgs" set create-vol2=1
if %create-vol1% == 0 set /a count-vol-create=0
if not %create-vol1% == 0 set /a count-vol-create=%count-vol-create%+=1
if %create-vol2% == 0 set /a count-vol-create=%count-vol-create%+=0
if not %create-vol2% == 0 set /a count-vol-create=%count-vol-create%+=1
echo %count-vol-create%
pause
But what I'm focusing on is this:
:boot.jfk
cls
echo MAINBOOTCONFIG
echo.
echo CHECKING FOR VOLUMES TO CREATE..
ping localhost -n 5 >nul
if exist "\SystemLiME" set create-vol1=0
) else (
if not exist "\SystemLiME" set create-vol1=1
if exist "\SystemLiME\pkgs" set create-vol2=0
) else (
if not exist "SystemLiME\pkgs" set create-vol2=1
if %create-vol1% == 0 set /a count-vol-create=0
if not %create-vol1% == 0 set /a count-vol-create=%count-vol-create%+=1
if %create-vol2% == 0 set /a count-vol-create=%count-vol-create%+=0
if not %create-vol2% == 0 set /a count-vol-create=%count-vol-create%+=1
echo %count-vol-create%
pause
Everytime I start this code, I get 'Missing operator.'
'Missing operator.'
'ECHO is off.'
'Press any key to continue...'
please help me!! :'( (btw, im new to stackoverflow :) )
Use this batch code as replacement for second posted batch code:
:boot.jfk
cls
echo MAINBOOTCONFIG
echo/
echo CHECKING FOR VOLUMES TO CREATE..
%SystemRoot%\System32\ping.exe localhost -n 5 >nul
if exist "\SystemLiME" ( set "CreateVol1=0" ) else ( set "CreateVol1=1" )
if exist "\SystemLiME\pkgs" ( set "CreateVol2=0" ) else ( set "CreateVol2=1" )
if %CreateVol1% == 0 ( set "CountVolCreate=0" ) else ( set /A "CountVolCreate+=1" )
if not %CreateVol2% == 0 set /A CountVolCreate+=1
echo %CountVolCreate%
pause
For the reason of using echo/ instead of echo. to output an empty line see DosTips forum topic
ECHO. FAILS to give text or blank line - Instead use ECHO/
Specify in batch files executables like ping with full path and with file extension to make the batch file independent on the current values of the environment variables PATH and PATHEXT.
See the answers on IF ELSE syntax error within batch file? and batch scripting - if exist ./sdcard/file.any using adb
The string after set /A is interpreted as arithmetic expression on which each whitespace/operator delimited string is interpreted automatically as variable name whose current value should be converted to a 32-bit signed integer for evaluation of the expression. Therefore it is not needed to use immediate variable expansion using %VariableName% or delayed variable expansion using !VariableName! in an arithmetic expression because just VariableName is enough even within a command block, except the variable name contains spaces or dashes or other operators.
For that reason it is not good to use variable names containing dashes when using such variables in an arithmetic expression as the dash could be interpreted as minus operator. The usage of CamelCase naming notation is in general best for variable names in batch files. Variables with CamelCase names can be easily read and searched/replaced in batch files and are surely never mixed up with text output with echo or within a remcomment line as for example on having the following command lines in a batch file:
#echo off
rem Assign batch file name to a variable.
set "BatchFileName=%~nx0"
echo Batch file name is: %BatchFileName%
set "BatchFileName="
An arithmetic expression should contain always only one equal sign. A syntax like variable=variable+=1 is definitely not right. Right is either variable=variable+1 or shorter variable+=1.
Read the answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? for an explanation why usage of set "variable=value" is recommended for the definition (or deletion) of an environment variable. On an arithmetic expression where whitespaces are just delimiters it is usually not needed to use syntax set /A "variable=expression", but there are exceptions like the one in line 9 of batch code above.
Please note that if exist "\SystemLiME" means if there is a directory or file with name SystemLiME in root of current drive. If you want to exclude file names and check for existence of a directory append a backslash on directory path. And if you want to check for existence of the directory in current directory remove the backslash at beginning of the path.
:boot.jfk
cls
echo MAINBOOTCONFIG
echo/
echo CHECKING FOR VOLUMES TO CREATE..
%SystemRoot%\System32\ping.exe localhost -n 5 >nul
if exist "SystemLiME\" ( set "CreateVol1=0" ) else ( set "CreateVol1=1" )
if exist "SystemLiME\pkgs\" ( set "CreateVol2=0" ) else ( set "CreateVol2=1" )
if %CreateVol1% == 0 ( set "CountVolCreate=0" ) else ( set /A "CountVolCreate+=1" )
if not %CreateVol2% == 0 set /A CountVolCreate+=1
echo %CountVolCreate%
pause
This batch code checks for existence of the directories SystemLiME and SystemLiME\pkgs in current directory in comparison to above batch code which checks for existence of file or directory SystemLiME and file or directory pkgs in directory SystemLiME in root of current drive.
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.
cls /?
echo /?
if /?
pause /?
ping /?
set /?

'for' loop variable not releasing on loop iterations

Been wrecking my brain all night trying to figure out why this isn't working, but one of my variables isn't releasing on the next iteration of my loop and I can't figure out why... The first pass of the loop seems to work fine, but the next iteration, the first variable gets locked and the script connects to the system that's already been configured.
I've been staring at this for a while now and no matter how I approach it, it still behaves badly. :/ The purpose is to read a text-string of a given file, and use it to modify (via Find and Replace (fnr.exe)) another file with several instances of the required data. I didn't have alot of luck with 'findstr' replacing so many instances of the text required so I went with a tool I've used before that seemed to work really well in it's previous scripting application...
Truth be told, I find myself stumbling with even the most basic code a lot of times, so any kind soul willing to impart some wisdom/assistance would be greatly appreciated!
Thanks in advance...
#ECHO ON
setlocal enabledelayedexpansion
> "%~dp0report.log" ECHO Batch Script executed on %DATE% at %TIME%
rem read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
SET lwn=
SET WKSTN=%%A
rem connect to workstation and read lwn.txt file
pushd "\\%WKSTN%\c$\"
IF ERRORLEVEL 0 (
FOR /F %%I in (\\%wkstn%\c$\support\lwn.txt) DO (
SET LWN=%%I
%~dp0fnr.exe --cl --dir "\\%WKSTN%\c$\support\folder\config" --fileMask "file.xml" --find "21XXXX" --replace "%%I"
IF ERRORLEVEL 0 ECHO Station %LWN%,Workstation %WKSTN%,Completed Successfully >> %~dp0report.log
IF ERRORLEVEL 1 ECHO Station %LWN%,Workstation %WKSTN%, A READ/WRITE ERROR OCCURRED >> %~dp0report.log
echo logwrite error 1 complete
popd
)
)
IF ERRORLEVEL 1 (
ECHO ,,SYSTEM IS OFFLINE >> %~dp0report.log
)
popd
set wkstn=
set lwn=
echo pop d complete
)
msg %username% Script run complete...
eof
The ! notation must be used on all variables that are changed inside the loop.
C:>type looptest.bat
#ECHO OFF
setlocal enabledelayedexpansion
rem read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
SET WKSTN=%%A
ECHO WKSTN is set to %WKSTN%
ECHO WKSTN is set to !WKSTN!
pushd "\\!WKSTN!\c$\"
ECHO After PUSHD, ERRORLEVEL is set to %ERRORLEVEL%
ECHO After PUSHD, ERRORLEVEL is set to !ERRORLEVEL!
IF !ERRORLEVEL! NEQ 0 (
ECHO ,,SYSTEM IS OFFLINE
) ELSE (
ECHO Host !WKSTN! is available
)
popd
)
EXIT /B 0
The workstations.txt file contained the following. (I should not give out actual host names.)
LIVEHOST1
DEADHOST1
LIVEHOST2
The output is...
C:>call looptest.bat
WKSTN is set to
WKSTN is set to LIVEHOST1
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 0
Host LIVEHOST1 is available
WKSTN is set to
WKSTN is set to DEADHOST1
The network path was not found.
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 1
,,SYSTEM IS OFFLINE
WKSTN is set to
WKSTN is set to LIVEHOST2
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 0
Host LIVEHOST2 is available
Although your code have several issues, the main one is the use of % instead of ! when you access the value of variables modified inside a for loop (although you already have the "enabledelayedexpansion" part in setlocal command). However, I noted that you sometimes use the FOR replaceable parameter (like in --replace "%%I") and sometimes you use the variable with the same value (%LWN%), so a simpler solution in your case would be to replace every %VAR% with its corresponding %%A for parameter.
I inserted this modification in your code besides a couple small changes that make the code simpler and clearer.
#ECHO ON
setlocal
> "%~dp0report.log" ECHO Batch Script executed on %DATE% at %TIME%
rem Read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
rem Connect to workstation and read lwn.txt file
pushd "\\%%A\c$\"
IF NOT ERRORLEVEL 1 (
FOR /F "usebackq" %%I in ("\\%%A\c$\support\lwn.txt") DO (
%~dp0fnr.exe --cl --dir "\\%%A\c$\support\folder\config" --fileMask "file.xml" --find "21XXXX" --replace "%%I"
IF NOT ERRORLEVEL 1 (
ECHO Station %%I,Workstation %%A,Completed Successfully >> %~dp0report.log
) ELSE (
ECHO Station %%I,Workstation %%A, A READ/WRITE ERROR OCCURRED >> %~dp0report.log
echo logwrite error 1 complete
)
)
) ELSE (
ECHO ,,SYSTEM IS OFFLINE >> %~dp0report.log
)
popd
echo pop d complete
)
msg %username% Script run complete...

Batch: ( was not expected at this time

I would like to know why do I get an error("( was not expected at this time") in this script.
for /f "delims=" %%t in (%cd%\Multitool\Multitool.txt) do (
set /p username=
set /p password=
set /p created=
)
if %created%==accountcreated ( goto CONTINUE ) else ( goto CREATE )
I have those lines of codes wich always get me an error: "( was not expected" at this time.
In my program I want to know if the user created an account already...
When the user creates an account I echo this in %cd%\Multitool\Multitool.txt :
(
echo %username%
echo %password%
echo accountcreated
) > %cd%\Multitool\Multitool.txt
So can you tell me why I get that error and how to correct it please?
Sorry for my bad english im french...
#ECHO OFF
SETLOCAL enabledelayedexpansion
:: set up logfile and password
SET "logfile=U:\q28542185.txt"
SET "password=dummypassword"
SET "myusername=dummyusername"
:: Dummy log entry
(
echo %myusername%
echo %password%
echo accountcreated
) > %logfile%
:: This is just to ensure the three variables are empty
FOR %%t IN (myusername password created) DO SET "%%t="
for /f "delims=" %%t in (%logfile%) do (
SET "myusername=!password!"
SET "password=!created!"
SET "created=%%t"
)
if %created%==accountcreated ( goto CONTINUE ) else ( goto CREATE )
:continue
ECHO got to continue
GOTO :eof
:create
ECHO got to create
GOTO :EOF
I used a file named q28542185.txt to receive the log information for my testing.
Note that username is a magic variable initialised by the syste, so I don't like changing it, so I've substituted myusername.
The first part sets up a dummy log file for testing.
The replacement for loop reads the log file and "ripples" the lines through the variablenames using the delayedexpansion facility, so that the variables are set in appropriate sequence.
Note that if created contains Spaces then your if statement should be modified to
if "%created%"=="accountcreated" ....
to allow correct parsing sequence IF token operator token

How do you keep input and design in the same line without it cutting out to the next line?

So i'm creating a batch file, that displays your directory and thats just one feature but anyways, you'll directory is going to change while in the batch file, and the therefore the directory text is going to change...
So here's an example of what it does...
|----------------------------------|
|>>C:\Users\Joel\..................|
|----------------------------------|
When you change your directory it looks like:
|----------------------------------|
|>> C:\Users\Joel\Desktop\.................|
|----------------------------------|
How do I make it so it takes how ever many letters than takes it away from the spaces?
Please help?
You want to pad a string to a fixed length. The simple strategy is to create a variable containing your string plus more than enough pad characters to reach your limit. Then use a substring operation to trim the string to the desired length. I modified the algorithm slightly to preserve the entire string if it is already greater than or equal to the desired length.
#echo off
:: Initialize
setlocal enableDelayedExpansion
set maxLen=50
set "pad="
set "div="
for /l %%N in (1 1 %maxLen%) do (
set "pad=!pad!."
set "div=!div!-"
)
set "div=|!div!|"
:: Test the display
pushd "c:\Users\Joel"
call :displayCurrentDirectory
pushd "c:\Users\Joel\Desktop"
call :displayCurrentDirectory
exit /b
:displayCurrentDirectory
setlocal
set "txt=>>!cd!\"
if "!txt:~%maxLen%,1!" equ "" (
set "txt=!txt!!pad!"
set "txt=!txt:~0,%maxLen%!"
)
echo !div!
echo ^|!txt!^|
echo !div!
echo(
exit /b
Here is a version that uses multiple lines of fixed width to force the string to fit within the alloted horizontal space.
#echo off
:: Initialize
setlocal enableDelayedExpansion
set maxLen=15
set "pad="
set "div="
for /l %%N in (1 1 %maxLen%) do (
set "pad=!pad!."
set "div=!div!-"
)
set "div=|!div!|"
:: Test the display
pushd "c:\Users\Joel"
call :displayCurrentDirectory
pushd "c:\Users\Joel\Desktop"
call :displayCurrentDirectory
exit /b
:displayCurrentDirectory
setlocal
echo !div!
set "txt=>>!cd!\"
:loop
if "!txt:~0,%maxLen%!" neq "!txt!" (
echo ^|!txt:~0,%maxLen%!^|
set "txt=!txt:~%maxLen%!"
goto :loop
)
set "txt=!txt!!pad!"
set "txt=!txt:~0,%maxLen%!"
echo ^|!txt!^|
echo !div!
echo(
exit /b
Take a look at Improved :Format, new :FormatVar and :FormatColor functions
for a more general purpose routine to format text. It allows left and right justification of text.

Resources