If syntax in windows batch - windows

I'm trying to make this work in a windows batch file:
if not exist "%~n1.ext" (
set /P z="PROMPT (y,n)?"
if /i "%z%" == "y" (
echo if is working
)
)
But no matter what the input is, it never goes into the echo part. Is there something wrong in the syntax?

When you use a variable inside a block (between (and ), you need to enable delayed expansion:
setlocal enabledelayedexpansion
set var=hello
if "a"=="a" (
set var=world
echo %var% !var!
)

Stephan is correct, you need to use enabledlayedexpansion when using a nested variable. Here's your code with that syntax (replacing % with ! when using such variables):
setlocal enabledelayedexpansion
if not exist "%~n1.ext" (
set /P z="PROMPT (y,n)?"
if /i "!z!" == "y" (
echo if is working
)
)

Related

Windows batch if not equal do not execute if clause

I have a .bat file to execute in a windows server 2012 R2 machine. Problem is that I need to add an "if not equals" but when I try to execute it the code does not work.
I know that problem is in the if because I tried without the if and it works.
setlocal enabledelayedexpansion
set anno=%date:~6,4%
set mese=%date:~3,2%
set giorno=%date:~0,2%
set ore=%time:~0,2%
set minuti=%time:~3,2%
set secondi=%time:~6,2%
e:
mkdir "E:\Presenze\%anno%%mese%%giorno%%ore%%minuti%%secondi%"
move \\gbjob09\Info-Bit\Sql_Ges\PRESENZE\2019\*.old "E:\Presenze\%anno%%mese%%giorno%%ore%%minuti%%secondi%"
cd "E:\Presenze\%anno%%mese%%giorno%%ore%%minuti%%secondi%"
for /f %%a in ('dir /b *.old') do (
set originalname=%%a
set timbrnumber=!originalname:~4,4!
if NOT %timbrnumber% == "0584" (
if NOT %timbrnumber% == "0585" (
set shortname=9!originalname:~4,4!
echo !shortname!
for /f "tokens=*" %%b in (%%a) do (
echo %%b!shortname!>> "Presenze.txt"
)
)
)
)
copy "E:\Presenze\%anno%%mese%%giorno%%ore%%minuti%%secondi%\Presenze.txt" "E:\Presenze\presenze.txt"
exit
Like you can see the problem is only this really small part:
if NOT %timbrnumber% == "0584" (
I already tried to remove the double if but it still does not work.
I know that the old code (this one) works:
setlocal enabledelayedexpansion
set anno=%date:~6,4%
set mese=%date:~3,2%
set giorno=%date:~0,2%
set ore=%time:~0,2%
set minuti=%time:~3,2%
set secondi=%time:~6,2%
e:
mkdir "E:\Presenze\%anno%%mese%%giorno%%ore%%minuti%%secondi%"
move \\gbjob09\Info-Bit\Sql_Ges\PRESENZE\2019\*.old "E:\Presenze\%anno%%mese%%giorno%%ore%%minuti%%secondi%"
cd "E:\Presenze\%anno%%mese%%giorno%%ore%%minuti%%secondi%"
for /f %%a in ('dir /b *.old') do (
set originalname=%%a
set shortname=9!originalname:~4,4!
echo !shortname!
for /f "tokens=*" %%b in (%%a) do (
echo %%b!shortname!>> "Presenze.txt"
)
)
copy "E:\Presenze\%anno%%mese%%giorno%%ore%%minuti%%secondi%\Presenze.txt" "E:\Presenze\presenze.txt"
exit
I only added a variable:
set timbrnumber=!originalname:~4,4!
that should work (it shouold get 4 characters after exclude 4 characters of the originalname variable) like shortname print 9 and 4 characters after exclude 4 characters of the originalname variable, but I'm not really sure because I don't really know windows batch (this is the first time that I use it and the original code was not mine).
After that I only need to check if timbrnumber is not "0584" and it's not "0585" but everything I tried was a fail.
In short, as your variable is being set and used inside a code block and double quoting is required both sides of the ==, this section should fix your issue:
if NOT "1!timbrnumber! == "10584" (
if NOT "!1timbrnumber!" == "10585" (
which can be better written as:
if 1!timbrnumber! neq 10584 (
if 1!timbrnumber! neq 10585 (
Another observation, your time:
set ore=%time:~0,2%
set minuti=%time:~3,2%
set secondi=%time:~6,2%
can be better created as:
set mytime=%time::=%
echo %mytime:0,6%

Windows Batch File Expansion Variables

I'm having an issue with a Batch File. Basically, I would like to seek ahead by 1 in a for loop and use that value to reference the parameter at that position and then assign it to a variable which I can then use later, see code sample below.
Example: set myVar=%1+1
Where %1 is param name, and %2 would be param value.
::Batch file sample
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set count=0
set HasParam=0
set ParamValue="null"
set paramValPos=0
for %%i in (%*) do (
set /a count=!count!+1
if /i "%%i"=="MyParam" (
set /a HasParam=1
set /a paramValPos=!count!+1
::The next line is where I Need Help, it's currently incorrect!
set ParamValue=%!paramValPos!
)
)
if %HasParam%==1 (
echo "Parameter Value: %ParamValue%"
)
pause
Example call from command line: prog.bat MyParam=5
Now I know that I can access it at pos 2, like:
::Sample batch file
#echo off
echo "Param Name: %1"
echo "Param Value: %2"
pause
However, this is not what I want, as there are more parameters and some are optional and they can also be passed in, in any order.
Thanks for your assistance.
call set "ParamValue=%%!paramValPos!"
If I understood your request correctly, you want the next parameter after a given one. The Batch code below do that:
#echo off
set "HasParam="
set "ParamValue="
for %%i in (%*) do (
if defined HasParam (
if not defined ParamValue (
set ParamValue=%%i
)
) else if /i "%%i"=="MyParam" (
set HasParam=yes
)
)
if defined HasParam (
echo "Parameter Value: %ParamValue%"
)
pause

How to package all my functions in a batch file as a seperate file?

My question is related to this question. I have several bunch of actions that need to be executed from a batch file and I would like to model them as functions and call from a master sequence. From the above question, it is clear that I can do this with the call syntax
call:myDosFunc
My question is that can I place all these functions in a seperate batch file (functions.bat) and somehow 'include' that in the main batch file and call them? Another option would be to utilize the possibility to invoke functions.bat from main.bat with the call syntaxt, but I'm not sure if I can invoke that with a specific function instead of executing the whole batch file.
In short, I'm looking for something similar to the C programming world where my functions reside in a DLL and the main program contains only the high-level logic and calls the functions from the DLL.
I think a routing function in the beginning of a batch file is not that ugly.
You can use something like this at the beginning of a "libbatch.cmd"
call:%*
exit/b
:func1
[do something]
exit/b
:func2
[do something else]
exit/b
Now you can call func2 from another batch with:
call libbatch.cmd func2 params1 param2 ... paramN
this also preserves the errorlevel "thrown" by func2 (exit/b hands over the current errorlevel).
With the second call instead of a goto you ensure that "%1"=="param1" and not func2.
And call will not terminate the batch file if the label does not exist, it simply sets the errorlevel to 1 and puts an error message to 2 (errorout), which could be redirected to nul.
Explanation: %* contains all parameters, so in the example the first line translates to:
call:func2 params1 param2 ... paramN
Here is a simple example of how it might be done.
The function script is called with the name of the function as the first argument, and function arguments as arg2, arg3, ...
Assuming it is called properly, the script shifts the arguments and performs GOTO to the original arg1. Then the function has its arguments starting with the new arg1. This means you can take already written routines and plop them in the utility without having to worry about adjusting the parameter numbers.
The script gives an error if the function argument is not supplied, or if the function argument does not match a valid label within the script.
#echo off
if "%~1" neq "" (
2>nul >nul findstr /rc:"^ *:%~1\>" "%~f0" && (
shift /1
goto %1
) || (
>&2 echo ERROR: routine %~1 not found
)
) else >&2 echo ERROR: missing routine
exit /b
:test1
echo executing :test1
echo arg1 = %1
exit /b
:test2
echo executing :test2
echo arg1 = %1
echo arg2 = %2
exit /b
:test3
echo executing :test3
echo arg1 = %1
echo arg2 = %2
echo arg3 = %3
exit /b
I prefer the GOTO approach that I used above. Another option is to use CALL instead, as Thomas did in his answer.
For a working example of a usefull library of batch functions that uses the CALL technique, see CHARLIB.BAT, a library of routines for processing characters and strings within a batch file. A thread showing development of the library is available here
I wrote CharLib.bat a few years ago. Were I to write it today, I would probably use GOTO instead of CALL.
The problem with introducing a CALL is that it creates issues when passing string literals as parameters. The extra CALL means that a string literal containing % must have the percents doubled an extra time. It also means unquoted poison characters like & and | would need to be escaped an extra time. Those two issues can be addressed by the caller. But the real problem is that each CALL doubles up quoted carets: "^" becomes "^^". There isn't a good way to work around the caret doubling problem.
The problems with the extra CALL don't impact CharLib.bat because string values are passed by reference (variable name) and not as string literals.
The only down side to using GOTO with SHIFT /1 is that you cannot use %0 to get the name of the currently executing routine. I could have used SHIFT without the /1, but then you wouldn't be able to use %~f0 within a routine to get the full path to the executing batch file.
You can use this format - and launch it like this:
call mybat :function4 parameternumber2 parameternumber3
this would be one way of using a library
#echo off
goto %1
:function1
REM code here - recursion and subroutines will complicate the library
REM use random names for any temp files, and check if they are in use - else pick a different random name
goto :eof
:function2
REM code here - recursion and subroutines will complicate the library
REM use random names for any temp files, and check if they are in use - else pick a different random name
goto :eof
:function3
REM code here - recursion and subroutines will complicate the library
REM use random names for any temp files, and check if they are in use - else pick a different random name
goto :eof
:function4
REM code here - recursion and subroutines will complicate the library
REM use random names for any temp files, and check if they are in use - else pick a different random name
goto :eof
You may use an interesting trick that avoids most of the problems that other methods have when they try to make the library functions available to the main program and it is much faster. The only requisites to use this trick are:
The library functions must be called from inside a code block in the main file, and
In that code block no main file functions are called.
The trick consist in "switch the context" of the running Batch file in a way that the library file becomes the running Batch file; this way, all the functions in the library file becomes available to the main code block with no additional processing. Of course, the "context" of the running Batch file must be switched back to the main file before the code block ends.
The way to "switch the context" is renaming the library file with the same name of the running main file (and renaming the main file to another name). For example:
(
rem Switch the context to the library file
ren main.bat orig-main.bat
ren library.bat main.bat
rem From this point on, any library function can be called
. . . .
rem Switch back the context to the original one
ren main.bat library.bat
ren orig-main.bat main.bat
)
EDIT: Working example added
I copied the example below from the screen. Tested in Windows 8, but I also used this method in Win XP:
C:\Users\Antonio\Documents\test
>type main.bat
#echo off
(
rem Switch the context to the library file
ren main.bat orig-main.bat
ren library.bat main.bat
rem From this point on, any library function can be called, for example:
echo I am Main, calling libFunc:
call :libFunc param1
echo Back in Main
rem Switch back the context to the original one
ren main.bat library.bat
ren orig-main.bat main.bat
)
C:\Users\Antonio\Documents\test
>type library.bat
:libFunc
echo I am libFunc function in library.bat file
echo My parameter: %1
exit /B
C:\Users\Antonio\Documents\test
>main
I am Main, calling libFunc:
I am libFunc function in library.bat file
My parameter: param1
Back in Main
I'm not sure of the context of the original question, but this might be a case where switching to something like WSH with VBScript or WPS, or any other console-capable scripting other than batch files. I will answer the original question, but first.. a little background and understanding..
The command line/console mode of DOS and Windows is usually either COMMAND.COM or CMD.EXE, which isn't well-geared towards scripting/programming logic. Rather, they're geared towards executing commands and programs, and batch files were added to commonly used sequences of commands to wrapped up in a single typed command. For example, you may have an old DOS game you play that needs the following commands every time, so it's packaged as a batch file:
#EHO OFF
#REM Load the VESA driver fix..
VESAFIX.EXE
#REM Load the joystick driver..
JOYSTICK.COM
#REM Now run the game
RUNGAME.EXE
Many people tend to view the entire batch file as one atomic unit--But it's not. The command interpreter (COMMAND.COM or CMD.EXE) will merely act like you manually typed those lines, one by one, each time you run the batch file. It really has no solid concept of lexica and scoping like a regular programming/scripting language would--that is, it doesn't maintain much extra meta-data like a call stack, et cetera. What little it does maintain is more added like it's an afterthought rather than built-in to batch file from the beginning.
Once you shift the gears in your thinking, however, you can often overcome this limitation using various tricks and techniques to emulate more powerful scripting/programming languages; But you still have to remember that batch files are still going to be limited, regardless.
Anyhow, one technique of using a batch file library is to create a batch file where the first parameter is used to indicate which function is being called:
CALL BATLIB.BAT FunctionName Parameter1 Parameter2 ...
This works well enough when the library is written with this in mind, so it'll know to skip the first argument, et cetera.
Using more modern version of CMD.EXE in Windows' systems allows the use of ":labels" in the CALL syntax, which can be useful if you want to limit the parameter scope (which allows you to use %* for "all arguments", for example), like this:
CALL :LABEL Parameter1 Paramater2 ...
(from within the same batch file or ...)
CALL BATLIB.BAT :LABEL Parameter1 Parameter2 ...
A few notes about that, though.. In the first form, the :LABEL must be already within the current batch file. It will create a new "batch context" within CMD.EXE where %*, %1, %2, et cetera are matched to the parameters. But you'll also have to provide some kind of return/exit logic to return/exit from that context back to the calling context.
In the second form, CMD.EXE does not really recognize that you are passing it a label, so your batch file library will have to expect it and handle it:
#ECHO OFF
CALL %*
This works because the command interpreter replaces the %* before it even attempts to parse the CALL command, so after variable expansion, the CALL command would see the :LABEL as if it were hard-coded. This also creates a situation where CMD.EXE creates yet another batch context, so you'll have to make sure to return/exit from that context twice: Once for the current library context, again to get back to the original CALL.
There are still other ways to do a batch file library, mixing and matching the above techniques, or using even more complex logic, using GOTO's, et cetera. This is actually such a complex topic that there are entire sections of books written on the topic, much more than I want to type in a simple answer here!
And so far, I've mostly ignored other issues you will encounter: What if the CALL label does not exist? How will it be handled? What about environment variable expansion? when does it happen? How can you prevent it from happening too soon? What about using special DOS characters in the arguments/parameters? For example, how does the interpreter see a line like: CALL :ProcessPath %PATH%? (The answer to that is that CMD.EXE replaces the entire %PATH% before it even processes the CALL command. This can create issues if your path has spaces in it which can trip up how CALL processes the entire thing, as many Windows' %PATH% variables do.. C:\Program Files.. for example..)
As you can see, things are getting complicated and messy very quickly.. And you have to stop thinking like a programmer and start thinking like COMMAND.COM/CMD.EXE, which pretty much only sees one single line at a time, not the whole batch file as an atomic unit. In fact, here's an example to help you really grasp the way it works..
Create a folder, C:\testing, and put the following batch, file called "oops.bat", in it:
#ECHO OFF
ECHO Look mom, no brain!
PAUSE
ECHO Bye mom!
Now open a console window and run it, but let it sit there at the PAUSE:
C:\testing>oops.bat
Look mom, no brain!
Press any key to continue . . .
While it's sitting at the PAUSE, open oops.bat in your text editor and change it to:
#ECHO OFF
ECHO Look mom, no brain!?
ECHO Oops!
PAUSE
ECHO Bye mom!
Save it, then switch back to your console window and press any key to continue running the batch file:
'ops!' is not recognized as an internal or external command,
operable program or batch file.
Press any key to continue . . .
Bye mom!
c:\testing>
Whoa.. see that error there? That happened because we edited the batch file while it was still being run by CMD.EXE, but our edits changed where in the batch file CMD.COM thought it was. Internally, CMD.EXE maintains a file pointer indicating the start of the next character to process, which in this case would have been the byte right after the line with the PAUSE (and the CRLF) on it. But when we edited it, it changed the location of the next command in the batch file, but CMD.EXE's pointer was still in the same place. In this case, it was pointing to the byte position right in the middle of the "ECHO Oops!" line, so it tried to process "ops!" as a command after the PAUSE.
I hope this makes it clear that COMMAND.COM/CMD.EXE will always see your batch files as a stream of bytes, not as a logical block, subroutines, et cetera, like a script language or compiler would. This is why batch file libraries are so limited. It makes it impossible to "import" a library in to a currently running batch file.
Oh, and I just had another thought.. In modern Windows' CMD.EXE, you can always create a batch file which creates a temporary batch file on the fly, then calls it:
#ECHO OFF
SET TEMPBAT=%TEMP%\TMP%RANDOM:~0,1%%RANDOM:~0,1%%RANDOM:~0,1%%RANDOM:~0,1%.BAT
ECHO #ECHO OFF > %TEMPBAT%
ECHO ECHO Hi Mom! I'm %TEMPBAT%! >> %TEMPBAT%
ECHO Hello, world, I'm %~dpnx0!
CALL %TEMPBAT%
DEL %TEMPBAT%
This effectively creates a temporary batch file in your temporary directory, named TMP####.BAT (where the #'s are replaced by random numbers; The %RANDOM:~0,1% means take the first digit of the number returned by %RANDOM%--we only wanted one single digit here, not the full number that RANDOM returns..), then ECHO's "Hello, World," followed by it's own full name (the %~dpnx0 part), CALLs the temporary batch file, which in turn ECHO's "Hi Mom!" followed by it's own [random] name, then returns to the original batch file so it can do whatever cleanup is needs, such as deleting the temporary batch file in this case.
Anyhow, as you can see by the length of this post, this topic really is not a simple one. There's dozens or more web pages out on the web with tons of batch file tips, tricks, et cetera, many of which go in to depth about how to work with them, create batch file libraries, what to watch out for, how to pass arguments by reference vs. by value, how to manage when and where variables get expanded, and so on.
Do a quick Google search for "BATCH FILE PROGRAMMING" to find many of them, and you can also check out Wiki and WikiBooks, SS64.com, robvanderwoude.com and even DMOZ's directory with more resources.
Good luck!
Here's a cmd batch script, which imports files or files in folders (recursivelly) into the main script:
#echo off
REM IMPORT - a .cmd utility for importing subroutines into the main script
REM !!! IN ORDER TO FUNCTION CORRECTLY: !!!
REM !!! IMPORT MUST BE CALLED INSIDE A DISABLED DELAYED EXPANSION BLOCK/ENVIRONMENT (DEFAULT) !!!
rem \\// Define import file mask here:
rem If mask is not defined outside "import.cmd":
if not defined mask (
set "mask=*.cmd; *.bat"
)
rem //\\ Define import file mask here:
rem Detect if script was started from command line:
call :DetectCommandLine _not_started_from_command_line
if "%~1" == "/install" (
set "import_path=%~dp0"
call :EscapePathString import_path import_path_escaped
)
if not "%~1" == "" (
if /i not "%~1" == "end" (
if "%~1" == "/?" (
call :DisplayHelp
) else (
if "%~1" == "/install" (
echo Installing
set "_first_time="
rem This should get into the Autorun registry key: path %path%;"...\import.cmd"
rem If you want, other commands can be added to the left or to the right of this command, unified as a block with a "&" command operator
REG ADD "HKCU\Software\Microsoft\Command Processor" /v AutoRun /t REG_SZ /d "path %%path%%;"""%import_path_escaped%""||(
echo ERROR: Cannot install import: cannot write to the registry^!
echo You can try to manually add the "import.cmd" path in the "PATH" variable or use pushd ^(see help^).
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
)
echo.
echo Done. The install directory was set to:
echo "%import_path%"
echo and was added to the PATH environment variable. You can add other desired programs into this directory.
echo.
echo Please note that the console needs to be closed and reopened in order for the changes to take effect^!
echo.
) else (
if not defined _first_time (
set _first_time=defined
set /a count=0
if "%_DISPLAY_WARNING%" == "true" (
echo.
echo WARNING: CMD_LIBRARY was reset to "", because previously it gave an error^!
echo.
)
echo Loading list to import...
)
REM build import files list
set /a count+=1
call set "_import_list_%%count%%=%%~1"
)
)
)
) else (
call :DisplayHelp
)
if /i "%~1" == "end" (
set "_first_time="
echo Done.
echo Analyzing...
rem set "_main_program=%~dpnx2"
if not exist "%~dpnx2" (
echo ERROR: Second parameter, after "import end", must be a valid file path - see help^!>>&2
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
)
)
if /i "%~1" == "end" (
set "_main_batch_script=%~dpnx2"
rem \\// Define output filename here:
rem set "_output_filename=tmp0001_%~n2.cmd"
set "_output_filename=tmp0001.cmd"
rem //\\ Define output filename here:
)
if /i "%~1" == "end" (
rem Check all paths not to be UNC:
setlocal EnableDelayedExpansion
set "_error=false"
call :TestIfPathIsUNC _main_batch_script _result
if "!_result!" == "true" (
set "_error=true"
echo.
echo ERROR: UNC paths are not allowed: Second parameter, after "import end", must not be a UNC path^^^! Currently it is: "!_main_batch_script!">>&2
)
set "_CMD_LIBRARY_error=false"
call :TestIfPathIsUNC CMD_LIBRARY _result
if "!_result!" == "true" (
set "_error=true"
set "_CMD_LIBRARY_error=true"
echo.
echo ERROR: UNC paths are not allowed: CMD_LIBRARY variable must not contain a UNC path^^^! Currently, it is set to: "!CMD_LIBRARY!".>>&2
)
for /l %%i in (1,1,!count!) do (
call :TestIfPathIsUNC _import_list_%%i _result
if "!_result!" == "true" (
set "_error=true"
echo.
echo ERROR: UNC paths are not allowed: The import path: "!_import_list_%%i!" is a UNC path^^^!>>&2
)
)
if "!_error!" == "true" (
echo.
echo Errors were ecountered^^^!
if "!_CMD_LIBRARY_error!" == "true" (
endlocal
set "_CMD_LIBRARY_error=true"
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
)
if /i "%~1" == "end" (
rem Check all paths not to contain "*" and "?" wildcards:
set "_asterisk=*"
set "_qm=?"
setlocal EnableDelayedExpansion
set "_error=false"
call :TestIfStringContains _main_batch_script _asterisk _result1
call :TestIfStringContains _main_batch_script _qm _result2
if "!_result1!" == "true" (
set "_error=true"
)
if "!_result2!" == "true" (
set "_error=true"
)
if "!_error!" == "true" (
echo.
echo ERROR: The use of "*" or "?" wildcards is not supported by import: Second parameter, after "import end", must not contain "*" or "?" wildcards^^^! Currently it is: "!_main_batch_script!". Instead, you can set the mask with a set of name and extension masks sepparated by semicolon, like: set mask="*.cmd; *.bat">>&2
)
set "_CMD_LIBRARY_error=false"
call :TestIfStringContains CMD_LIBRARY _asterisk _result1
call :TestIfStringContains CMD_LIBRARY _qm _result2
if "!_result1!" == "true" (
set "_error=true"
)
if "!_result2!" == "true" (
set "_error=true"
)
if "!_error!" == "true" (
set "_error=true"
set "_CMD_LIBRARY_error=true"
echo.
echo ERROR: The use of "*" or "?" wildcards is not supported by import: CMD_LIBRARY variable must not contain "*" or "?" wildcards^^^! Currently, it is set to: "!CMD_LIBRARY!". Instead, you can set the mask with a set of name and extension masks sepparated by semicolon, like: set mask="*.cmd; *.bat">>&2
)
for /l %%i in (1,1,!count!) do (
call :TestIfStringContains _import_list_%%i _asterisk _result1
call :TestIfStringContains _import_list_%%i _qm _result2
if "!_result1!" == "true" (
set "_error=true"
)
if "!_result2!" == "true" (
set "_error=true"
)
if "!_error!" == "true" (
set "_error=true"
echo.
echo ERROR: The use of "*" or "?" wildcards is not supported by import: The import path: "!_import_list_%%i!" must not contain "*" or "?" wildcards^^^! Instead, you can set the mask with a set of name and extension masks sepparated by semicolon, like: set mask="*.cmd; *.bat">>&2
)
)
if "!_error!" == "true" (
echo.
echo Errors were ecountered^^^!
if "!_CMD_LIBRARY_error!" == "true" (
endlocal
set "_CMD_LIBRARY_error=true"
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
)
if /i "%~1" == "end" (
pushd "%~dp2"
call set "_output_dir=%%CD%%"
popd
)
if /i "%~1" == "end" (
if not defined CMD_LIBRARY (
set CMD_LIBRARY_CASE=IMPORT.CMD
set "BASE=%~dpnx0\.."
pushd "%~dpnx0"\..\
REM \\// Define CMD LIBRARY here ("." is relative to "import.cmd" parent directory):
REM if CMD_LIBRARY is not defined outside import.cmd, "." (used here) is related to import.cmd parent directory:
set "CMD_LIBRARY=."
REM //\\ Define CMD LIBRARY here ("." is relative to "import.cmd" parent directory):
) else (
set CMD_LIBRARY_CASE=MAIN.CMD
set "BASE=%~dpnx2\.."
REM if CMD_LIBRARY is defined outside the "import.cmd" script, "." (used in CMD_LIBRARY) is related to "main program" parent directory
pushd "%~dpnx2"\..
)
)
if /i "%~1" == "end" (
call :DeQuoteOnce CMD_LIBRARY CMD_LIBRARY
call set "CMD_LIBRARY_ORIGINAL=%%CMD_LIBRARY%%"
call :TestIfPathIsUNC CMD_LIBRARY_ORIGINAL _result
setlocal EnableDelayedExpansion
if "!_result!" == "true" (
set "_error=true"
echo.
echo ERROR: UNC paths are not allowed: CMD_LIBRARY variable must not contain a UNC path^^^! Currently, it is set to: "!CMD_LIBRARY_ORIGINAL!".>>&2
echo.
echo Errors were ecountered^^^!
endlocal
set "_CMD_LIBRARY_error=true"
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
call pushd "%%CMD_LIBRARY%%" >nul 2>nul&&(
call set "CMD_LIBRARY=%%CD%%"
)||(
call echo ERROR: Could not access directory CMD_LIBRARY=^"%%CMD_LIBRARY%%^"^!>>&2
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
popd
exit /b 1
)
)
if /i "%~1" == "end" (
setlocal EnableDelayedExpansion
set _error=false
pushd "!BASE!"
echo.
if "!CMD_LIBRARY_CASE!" == "IMPORT.CMD" (
echo CMD_LIBRARY was defined as: "!CMD_LIBRARY_ORIGINAL!" in the file "import.cmd" and was expanded to: "!CMD_LIBRARY!"
) else (
if "!CMD_LIBRARY_CASE!" == "MAIN.CMD" (
echo CMD_LIBRARY was defined as: "!CMD_LIBRARY_ORIGINAL!" outside "import.cmd" file "%~nx2" and was expanded to: "!CMD_LIBRARY!"
)
)
for /l %%i in (1,1,!count!) do (
if not exist "!_import_list_%%i!" (
if not exist "!CMD_LIBRARY!\!_import_list_%%i!" (
rem if first time:
if not "!_error!" == "true" (
echo.
echo Directory of "!CMD_LIBRARY!":
)
echo.
echo ERROR: element "!_import_list_%%i!" does not exist or is not accessible as a standalone file/dir or as a file/dir in the directory contained by "CMD_LIBRARY" variable^^^!>>&2
set _error=true
)
)
)
popd
if "!_error!" == "true" (
endlocal
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
endlocal
)
echo OK
echo.
)
set "_error=false"
if /i "%~1" == "end" (
echo Output file is: "%_output_dir%\%_output_filename%"
echo.
echo Importing...
echo.
(
type nul>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not write to file: "%_output_dir%\%_output_filename%"^!>>&2
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
)
echo Importing main script "%_main_batch_script%"
(
echo #set _import=defined
echo #REM Timestamp %date% %time%
echo.
)>>"%_output_dir%\%_output_filename%"
(
(
type "%_main_batch_script%"
)>>"%_output_dir%\%_output_filename%"
) 2>nul||(echo ERROR: Could not read file^!&set "_error=true">>&2)
(
echo.
echo.
)>>"%_output_dir%\%_output_filename%"
echo.
echo Directory of "%CMD_LIBRARY%":
if not "%CMD_LIBRARY_CASE%" == "MAIN.CMD" (
pushd "%BASE%"
)
if not defined mask (
rem If mask is not defined, import all file types:
set "mask=*"
)
for /l %%i in (1,1,%count%) do (
call set "_import_list_i=%%_import_list_%%i%%"
call :ProcedureImportCurrentFile
)
if not "%CMD_LIBRARY_CASE%" == "MAIN.CMD" (
popd
)
)
if "%~1" == "end" (
if "%_error%" == "true" (
echo.
echo Errors were ecountered^!
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
echo Done^!
)
call popd
popd
rem Clean up
call :CleanUp
rem Detect if script was started from command line:
call :DetectCommandLine _not_started_from_command_line
)
if "%~1" == "end" (
if "%_not_started_from_command_line%" == "0" (
set "_import="
) else (
echo.
echo Starting program...
echo.
rem Start "resulting" program:
"%_output_dir%\%_output_filename%"
)
)
goto :eof
REM \\\/// Next subroutines use jeb's syntax for working with delayed expansion: \\\///
:CleanUp
(
setlocal EnableDelayedExpansion
)
(
endlocal
if "%_CMD_LIBRARY_error%" == "true" (
set "CMD_LIBRARY="
set "_DISPLAY_WARNING=true"
) else (
set "_DISPLAY_WARNING=false"
)
set "_first_time="
for /l %%i in (1,1,%count%) do (
set "_import_list_%%i="
)
rem optional:
set "count="
set "import_path="
rem set "_output_dir="
set "_error="
set "_main_batch_script="
rem set "_output_filename="
rem set "_import="
set "mask="
exit /b
)
:GetStrLen - by jeb - adaptation
(
setlocal EnableDelayedExpansion
set "s=!%~1!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
)
(
endlocal
set "%~2=%len%"
exit /b
)
:EscapePathString
(
setlocal EnableDelayedExpansion
set "string=!%~1!"
call :GetStrLen string string_len
set /a string_len-=1
for /l %%i in (0,1,!string_len!) do (
rem escape "^", "(", ")", "!", "&"
if "!string:~%%i,1!" == "^" (
set "result=!result!^^^^"
) else (
if "!string:~%%i,1!" == "(" (
set "result=!result!^^^("
) else (
if "!string:~%%i,1!" == ")" (
set "result=!result!^^^)"
) else (
if "!string:~%%i,1!" == "^!" (
set "result=!result!^^^!"
) else (
if "!string:~%%i,1!" == "&" (
set "result=!result!^^^&"
) else (
if "!string:~%%i,1!" == "%%" (
set "result=!result!%%"
) else (
set "result=!result!!string:~%%i,1!"
)
)
)
)
)
)
)
)
(
endlocal
set "%~2=%result%"
exit /b
)
:PressAnyKey
set /p=%*<nul
pause>nul
goto :eof
:DeQuoteOnce
(
setlocal EnableDelayedExpansion
set "string=!%~1!"
if "!string!" == """" (
endlocal
set "%~2=%string%"
exit /b
)
rem In order to work with " we replace it with a special character like < > | that is not allowed in file paths:
set "string=!string:"=^<!"
if "!string:~0,1!" == "<" (
if "!string:~-1,1!" == "<" (
set "string=!string:~1,-1!"
)
)
rem restore " in string (replace < with "):
set "string=!string:<="!"
)
(
endlocal
set "%~2=%string%"
exit /b
)
:TestIfPathIsUNC
(
setlocal EnableDelayedExpansion
set "_current_path=!%~1!"
set "_is_unc_path=true"
if defined _current_path (
if "!_current_path:\\=!" == "!_current_path!" (
set "_is_unc_path=false"
)
) else (
set "_is_unc_path=false"
)
)
(
endlocal
set "%~2=%_is_unc_path%"
exit /b
)
:TestIfStringContains
(
setlocal EnableDelayedExpansion
echo "!%~1!"|find "!%~2!">nul 2>nul
set "_error_code=!ERRORLEVEL!"
)
(
endlocal
if "%_error_code%" == "0" (
set "%~3=true"
) else (
set "%~3=false"
)
exit /b
)
REM ///\\\ The subroutines above use jeb's syntax for working with delayed expansion: ///\\\
:DetectCommandLine
setlocal
rem Windows: XP, 7
for /f "tokens=*" %%c in ('echo "%CMDCMDLINE%"^|find "cmd /c """ /c') do (
set "_not_started_from_command_line=%%~c"
)
if "%_not_started_from_command_line%" == "0" (
rem Windows: 10
for /f "tokens=*" %%c in ('echo "%CMDCMDLINE%"^|find "cmd.exe /c """ /c') do (
set "_not_started_from_command_line=%%~c"
)
)
endlocal & (
set "%~1=%_not_started_from_command_line%"
)
goto :eof
:ProcedureImportCurrentFile
setlocal
set "cc="
if not exist "%_import_list_i%" (
set "_not_a_dir=false"
pushd "%CMD_LIBRARY%\%_import_list_i%\" 1>nul 2>&1||set "_not_a_dir=true"
call :GetStrLen CD _CD_len
)
if "%_not_a_dir%" == "false" (
setlocal EnableDelayedExpansion
if not "!CD:~-1,1!" == "\" (
endlocal
set /a _CD_len+=1
) else (
endlocal
)
popd
)
if not exist "%_import_list_i%" (
if "%_not_a_dir%" == "true" (
echo Importing file "%CMD_LIBRARY%\%_import_list_i%"
(
type "%CMD_LIBRARY%\%_import_list_i%">>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not read file^!>>&2
set "_error=true"
)
(
if not "%%i" == "%count%" (
echo.
echo.
) else (
echo.
)
)>>"%_output_dir%\%_output_filename%"
) else (
echo Importing dir "%_import_list_i%"
rem
pushd "%CMD_LIBRARY%\%_import_list_i%\"
set /a cc=0
for /r %%f in (%mask%); do (
set "_current_file=%%~dpnxf"
call set "r=%%_current_file:~%_CD_len%%%"
call echo Importing subfile "%%_import_list_i%%\%%r%%"
(
(
call type "%%_current_file%%"
)>>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not read file^!>>&2
set "_error=true"
)
(
echo.
echo.
)>>"%_output_dir%\%_output_filename%"
set /a cc+=1
)
popd
)
) else (
set "_not_a_dir=false"
pushd "%_import_list_i%\" 1>nul 2>&1||set "_not_a_dir=true"
call :GetStrLen CD _CD_len
)
if "%_not_a_dir%" == "false" (
setlocal EnableDelayedExpansion
if not "!CD:~-1,1!" == "\" (
endlocal
set /a _CD_len+=1
) else (
endlocal
)
popd
)
if exist "%_import_list_i%" (
if "%_not_a_dir%" == "true" (
echo Importing file "%_import_list_i%"
(
type "%_import_list_i%">>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not read file^!>>&2
set "_error=true"
)
(
if not "%%i" == "%count%" (
echo.
echo.
) else (
echo.
)
)>>"%_output_dir%\%_output_filename%"
) else (
rem
echo Importing dir "%_import_list_i%"
pushd "%_import_list_i%\"
set /a cc=0
for /r %%f in (%mask%); do (
set "_current_file=%%~dpnxf"
call set "r=%%_current_file:~%_CD_len%%%"
call echo Importing subfile "%%_import_list_i%%\%%r%%"
(
(
call type "%%_current_file%%"
)>>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not read file^!>>&2
set "_error=true"
)
(
echo.
echo.
)>>"%_output_dir%\%_output_filename%"
set /a cc+=1
)
popd
)
)
if "%cc%" == "0" (
echo No match^!
)
endlocal & (
set "_error=%_error%"
)
goto :eof
:DisplayHelp
echo IMPORT - a .cmd utility for importing subroutines into the main script
echo.
echo NOTES: 1. This utility assumes that command extensions are enabled (default) and that delayed expansion can be enabled;
echo ALSO IMPORT MUST BE CALLED INSIDE A DISABLED DELAYED EXPANSION BLOCK/ENVIRONMENT (DEFAULT);
echo These are necessary in order for it to function correctly.
echo 2. The use of UNC paths is not supported by import. As a workarround, you can mount a UNC path to a temporary drive using "pushd".
echo The use of "*" or "?" wildcards is not supported by import. Instead, you can set the mask with a set of name and extension masks sepparated by semicolon, like: set mask="*.cmd; *.bat"
echo When the "mask" variable is set, only the filenames having the extensions contained by it are matched at import.
echo.
echo Description:
echo import organizes your batch programs on common libraries of subroutines, that you can use in the future for other programs that you build; it also makes code editing and debugging easier.
echo.
echo Usage [1]:
echo import [flags]
echo.
echo [flags] can be:
echo /install - installs import into the registry, in the Command Processor AutoRun registry key ^(adds the current location of import into the PATH variable^).
echo /? - displays help ^(how to use import^)
echo.
echo Usage [2]:
echo What it does:
echo Concatenates ^(appends^) files content containing subroutines to the main program content using the following SYNTAX:
echo REM \\//Place this in the upper part of your script ^(main program)^ \\//:
echo.
echo #echo off
echo.
echo if not defined _import ^(
echo rem OPTIONAL ^(before the "import" calls^):
echo set "CMD_LIBRARY=^<library_directory_path^>"
echo.
echo import "[FILE_PATH1]filename1" / "DIR_PATH1"
echo ...
echo import "[FILE_PATHn]filenamen" / "DIR_PATHn"
echo import end "%%~0"
echo ^)
echo.
echo REM //\\Place this in the upper part of your script ^(main program)^ //\\:
echo.
echo "filename1" .. "filenamen" represent the filenames that contain the subroutines that the user wants to import in the current ^(main^) program. The paths of these files are relative to the directory contained in the CMD_LIBRARY variable.
echo.
echo "FILE_PATH1" .. "FILE_PATHn" represent the paths of these files.
echo.
echo "DIR_PATH1" .. "DIR_PATHn" represent directories paths in which to recursivelly search and import all the files of the type defined in the variable "mask"
echo.
echo CMD_LIBRARY is a variable that contains the directory path where your library of files ^(containing subroutines^) is found.
echo.
echo We denote the script that calls "import" as "the main script".
echo.
echo By default, if not modified in outside the import.cmd script, in the import.cmd script - CMD_LIBRARY is set to "." directory and is relative to the "import.cmd" parent directory.
echo If CMD_LIBRARY directory is modified outside the import.cmd script, CMD_LIBRARY is relative to the main script parent directory.
echo.
echo Note that only the last value of "CMD_LIBRARY" encountered before `import end "%%~0"` is taken into consideration.
echo.
echo import end "%%~0" - marks the ending of importing files and the start of building of the new batch file ^(named by default tmp0001.cmd, and located in the directory in which the main script resides^).
echo.
echo "%%~0" represents the full path of the main script.
goto :eof
To use it:
save it as import.cmd
call it with the /install flag in order to install it (does not require admin)
add a header like this at the begining of your main script that calls subroutines that are stored in other files - files that are going to be imported:
if not defined _import (
rem OPTIONAL (before the "import" calls):
set "CMD_LIBRARY=<library_directory_path>"
import "[FILE_PATH1]filename1" / "DIR_PATH1"
...
import "[FILE_PATHn]filenamen" / "DIR_PATHn"
import end "%~0"
)
To find out how to use it, simply call it with the /? flag.

What is wrong with this batch script?

I need a batch that reads a number from a file, increments it and saves it back into this file... This is what I came up with:
#ECHO OFF
SETLOCAL EnableDelayedExpansion
IF EXIST script\BUILDVERSION (
SET /p input = <script\BUILDVERSION
SET /a result=%input%+1
ECHO %result% > script\BUILDVERSION
) ELSE (
ECHO 0 > script\BUILDVERSION
)
At first it worked in a strange way, the result from reading the number from the file seemed to be a small random number, the result of the sum seemed random too... I don't know what I did, but now it doesn't even read the number from file into the variable...
Thanks in advance for help!
Instead of %input% and %result%, try using !input! and !result!. This seems to work better when using delayed expansion. Also, make sure you don't have any unnecessary spaces when reading from the file. You'll end up with:
#ECHO OFF
SETLOCAL EnableDelayedExpansion
IF EXIST script\BUILDVERSION (
SET /p input=<script\BUILDVERSION
SET /a result=!input!+1
ECHO !result! > script\BUILDVERSION
) ELSE (
ECHO 0 > script\BUILDVERSION
)

Batch delayed expansion not working

Ok, I'm getting crazy and I don't know what else to do, I've tried several things and nothing is working.
Look at this sample code (test.cmd):
setlocal enabledelayedexpansion enableextensions
set VAR=before
if "%VAR%" == "before" (
set VAR=after;
if "%VAR%" == "after" #echo If you see this, it worked
)
This is the generated output:
D:\>ver
Microsoft Windows [Version 6.1.7600]
D:\>test.cmd
D:\>setlocal enabledelayedexpansion enableextensions
D:\>set VAR=before
D:\>if "before" == "before" (
set VAR=after;
if "before" == "after"
)
D:\>
Am I doing something wrong?
This is just a test, the code I need uses variables too and needs delayed expansion, but it this simple test doesn't work the other wont work either (I've tried, I ended up with a simple example to test if it worked).
EDIT: New code and output:
test.cmd:
#echo off
setlocal enabledelayedexpansion enableextensions
set VAR=before
if "%VAR%" == "before" (
set VAR=after;
if "!VAR!" == "after" (
echo It worked.
) else (
echo It didn't work.
)
)
Output:
D:\>test.cmd
It didn't work.
D:\>
You have to use !var! for delayed expansion. %var% is always expanded on parse stage.
I.e., change your code to
setlocal enabledelayedexpansion enableextensions
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" #echo If you see this, it worked
)
At the beginning of the cmd prompt, you must type “CMD /V” OR “CMD /V:ON”
After this testing code should work
SETLOCAL EnableDelayedExpansion
Set "_var=first"
Set "_var=second" & Echo %_var% !_var!
You should be able to see output “first second”
dont use == , in batch you must use EQU
For Example write:
if %bla% EQU %blub% echo same
I found your problem.
set VAR=after;
delete ; from the code above

Resources