Something like a function/method in batch files? - windows

is there anything that mimicks a method like one knows it from Java, C# etc.? I have 5 lines of commands in a batch file, those 5 lines are used at more than one place inside the batch file. I can't use a goto, because depending on the errorlevel created by those 5 lines I have different actions that follow. I tried putting my 5 lines inside a batch file 5lines.bat, but the original batch file original.bat only calls 5lines.bat and doesn't execute the commands after the call to 5lines.bat ): That's how my original.bat looks like:
5lines.bat
echo this gets never called, how to make sure this gets called?
There's no exit or something like this in 5lines.bat! How can I make sure the line after 5lines.bat gets called?

You could use the call command :
call:myDosFunc
And then define the function this way :
:myDosFunc - here starts the function
echo. here the myDosFunc function is executing a group of commands
echo. it could do a lot of things
goto:eof
Source : Batch Functions

Just for completeness, you can also pass parameters to the function:
Function call
call :myDosFunc 100 "string val"
Function body
:myDosFunc
echo. Got Param#1 %~1
echo. Got Param#2 %~2
goto :eof

Placing the reusable functions into a separate batch file would certainly work to simulate a function.
The catch is that you have to use the call command in order to ensure that control returns to the caller after the second batch file finishes executing.
call 5lines.bat
echo this will now get called

Solution:
#ECHO OFF
call:header Start Some Operation
... put your business logic here
... make sure EXIT below is present
... so you don't run into actual functions without the call
call:header Operation Finished Successfully
EXIT /B %ERRORLEVEL%
:: Functions
:header
ECHO =================================================
ECHO %*
ECHO =================================================
EXIT /B 0
Important to put EXIT /B at the end of each function, as well as before function definitions start, in my example this is:
EXIT /B %ERRORLEVEL%

You could try to use the examples listed on DOS Batch - Function Tutorial
Alternatively, you could put the common lines into another batch file that you call from the main one

Coming from a Java background, I have tried to incorporate some familiar conventions when creating procedures for .bat scripts.
The script below demonstrates the definition of two procedures.
#ECHO OFF
SET firstInstanceVariable="Hello world!"
SET secondInstanceVariable="Good bye world!"
GOTO:MAIN
:firstMethodName
SETLOCAL ENABLEDELAYEDEXPANSION
SET firstArgumentPassedIn=%~1
SET secondArgumentPassedIn=%~2
ECHO %firstInstanceVariable%
ECHO "The first argument passed in was %firstArgumentPassedIn%"
ECHO "The second argument passed in was %secondArgumentPassedIn%"
ENDLOCAL
EXIT /B 0
:secondMethodName
SETLOCAL ENABLEDELAYEDEXPANSION
SET firstArgumentPassedIn=%~1
SET secondArgumentPassedIn=%~2
ECHO %secondInstanceVariable%
ECHO "The first argument passed in was %firstArgumentPassedIn%"
ECHO "The second argument passed in was %secondArgumentPassedIn%"
ENDLOCAL
EXIT /B 0
:MAIN
call:firstMethodName "The Quick Brown" "Fox Jumps Over"
call:secondMethodName "1 2 3 4" 3.14
Notice that an explicit GOTO:MAIN is necessary to skip over the procedure definitions.
This is because you must skip over the procedure before deciding to read it. Otherwise, the procedure will be executed.
The code below demonstrates a close Java-equivalent of the above .bat script.
public class MyObject {
private String firstInstanceVariable = "Hello world!";
private String secondInstanceVariable = "Good bye world!";
public void firstMethodName(Object... arguments) {
String firstArgumentPassedIn = arguments[0].toString();
String secondArgumentPassedIn = arguments[1].toString();
System.out.println(firstInstanceVariable);
System.out.format("The first argument passed in was %s", firstArgumentPassedIn);
System.out.format("The second argument passed in was %s", secondArgumentPassedIn);
}
public void secondMethodName(Object... arguments) {
String firstArgumentPassedIn = arguments[0].toString();
String secondArgumentPassedIn = arguments[1].toString();
System.out.println(secondInstanceVariable);
System.out.format("The first argument passed in was %s", firstArgumentPassedIn);
System.out.format("The second argument passed in was %s", secondArgumentPassedIn);
}
public static void main(String[] args) {
MyObject myObject = new MyObject();
myObject.firstMethodName("The Quick Brown", "Fox Jumps Over");
myObject.secondMethodName(new Integer[]{1,2,3,4}, 3.14);
}
}

Here's a 'hack' that will allow you to have "anonymous" functions in batch files:
#echo off
setlocal
set "anonymous=/?"
:: calling the anonymous function
call :%%anonymous%% a b c 3>&1 >nul
:: here the anonymous function is defined
if "%0" == ":%anonymous%" (
echo(
echo Anonymous call:
echo %%1=%1 %%2=%2 %%3=%3
exit /b 0
)>&3
::end of the anonymous function
The anonymous function block should be placed right after the call statement and must end with exit statement
the trick is that CALL internally uses GOTO and then returns to the line where the CALL was executed. With the double expansion GOTO help message is triggered (with %%/?%% argument) and then continues the script. But after it is finished it returns to the CALL - that's why the if statement is needed.

For another great tutorial on writing reusable batch file code -- see Richie Lawrence's excellent library.

I'm not sure if it was obvious from other answers but just to be explicit I'm posting this answer. I found other answers helpful in writing below code.
echo what
rem the third param gives info to which label it should comeback to
call :myDosFunc 100 "string val" ComeBack
:ComeBack
echo what what
goto :eof
:myDosFunc
echo. Got Param#1 %~1
echo. Got Param#2 %~2
set returnto=%~3
goto :%returnto%

Below may make it look like a function.
call :myFunc %para1% %para2%
:myFunc <para1> <para2>
echo %1
echo %2
EXIT /B
Example
#echo off
echo PROGRAM_NAME:%~nx0 Start
echo.
================================
SET debugMode=%1
call :myFunc1 %debugMode%
call :myFunc2 para1 "para2 hello"
================================
echo PROGRAM_NAME:%~nx0 End & pause>nul
EXIT /B
:: 👇 define the function under below
:myFunc1 <isDebug>
:: So that you can know the %1 means: isDebug.
if "%1" == "1" (
echo debug mode
)
EXIT /B
:myFunc2 <para1> <para2>
:: output: para1
echo %1
:: output: "para2 hello"
echo %2
EXIT /B

Related

If variable can't have spaces?

I am making an experimental program in batch for a simple chatting interface. In this one, I made a function where if there is the word r placed in chat, it ignores it and just redisplays the text file again. It works fine if I put r and it just refreshes, and if I put one word it works fine, but if I put a word and a space and another word, it breaks and shows the following error:
Chat(Put r for refresh):hey hi
hi was unexpected at this time.
Does anyone know how to fix this? Thanks.
Code:
#echo off
cls
cd %USERPROFILE%\Desktop\Chat
for /f "delims=" %%A in (chat.txt) do (
set %%A
)
echo %chatt%
echo %chatp%
echo %chatn%
cd %USERPROFILE%\Desktop\Chat\Servers\%chatt%
:1
cls
type %chatn%.chat
set /p in=Chat(Put r for refresh):
if %in% == r goto 1
echo %chatp%: %in%>>%chatn%.chat
goto 1
The usual way to deal with spaces in a string variables contents is to wrap it in quotes. This is the case here. When you use the variables contents with %in% the contents are inserted verbatim, so the suspect line would look like this:
if hey hi == r goto 1
It starts off okay if hey but then instead of seeing a comparison operator like == it sees hi and chokes. So wrap it all in quotes:
if "%in%" == "r" goto 1
That way it will be interpreted like
if "hey hi" == "r" goto 1
and the bat engine will know that "hey hi" should be treated as one entity.

Getting the return value from a cmd /c command in VBScript

In VBScript, the built-in Shell.Run method does not provide for output redirection, so the following workaround must be used:
Running command line silently with VbScript and getting output?
Dim retVal
retVal = WshShell.Run( "cmd /c ""commandGoesHere"" > c:\temp\output.txt", 0, True )
However returnValue will have the return value of cmd, not of commandGoesHere.
I thought I could check shell.Environment("ERRORLEVEL") but presumably this would also be cmd's return value, and not commandGoesHere.
...so how can I get commandGoesHere's return value and simultaneously redirect its output to another file?
returnValue = WScript.CreateObject("WScript.Shell").Run( _
"cmd /v /c (>""output.txt"" ""commandGoesHere"" & exit !errorlevel!)" _
, 0 _
, True _
)
Start the cmd instance with delayed expansion enabled (/v) and exit the cmd instance with the errorlevel set by the previous command.
Delayed expansion is needed because the cmd parser replaces all %var% read operations with the value inside the variables during the line/block parse phase. Without delayed expansion (%errorlevel%), the value to be returned by the exit command will be retrived before starting to execute the command. With delayed expansion (!errorlevel!) the value will be retrieved when the exit command is executed, after the commandGoesHere has ended.

how to pass comma separated parameter to batch file and use if else in batch file?

I have one batch file MyTest.bat
MyTest.bat
call "first.bat"
call "second.bat"
call "third.bat"
Now while executing MyTest.bat I will pass comma separated parameters like
call MyTest.bat first.bat,second.bat
now inside MyTest.bat I want to check which parameters are passed and based on those using if else condition
I want to execute internal statements.
for example something like it
MyTest.bat first.bat,second.bat
now inside
I will check
get all parameters list param[] = {first.bat,second.bat}
if param[i] == "first.bat"
{
call "first.bat"
}
else if param[i] == "second.bat"
{
call "second.bat"
}
else if param[i] == "third.bat"
{
call "third.bat"
}
// this assures that what parameters I passed only those statements gets executed not other.
The above code is just a pseudo code how can write actual MyTest.bat?
Next script requires another parameter order (list of batch names down to end all other parameters):
#ECHO OFF
SETLOCAL EnableExtensions
rem usage: 33955749.bat name address "first script", second, third
:loop
if "%~3" == "" goto :next
if exist "%~n3.bat" (
call "%~n3.bat" %1 %2
) else (
echo can't found file; failed call "%~n3.bat" %1 %2
)
shift /3
goto :loop
:next
For debugging purposes, prepare sample files "first script.bat" and second.bat; ensure that third.bat does not exist:
==> >"first script.bat" echo #echo %~nx0 parameters: %%*=%*
==> >second.bat echo #echo %~nx0 parameters: %%*=%*
==> 2>NUL del third.bat
Output (shows independency on used delimiters):
==> 33955749 name address "first script", second, third
first script.bat parameters: %*=name address
second.bat parameters: %*=name address
can't found file; failed call "third.bat" name address
==> 33955749 name address "first script" second; third
first script.bat parameters: %*=name address
second.bat parameters: %*=name address
can't found file; failed call "third.bat" name address
Another approach: fist parameter = list of comma-separated batch names surrounded with a pair of double quotes:
#ECHO OFF
SETLOCAL EnableExtensions
rem usage: 33955749b "first script,second,third" name address
rem no spaces surrounding commas or names
rem wrong: 33955749b " first script , second, third" would fail
set "_names=%~1"
set "_names=%_names:,=","%"
rem debug echo _names="%_names%"
for %%G in ("%_names%") do (
if exist "%%~dpnG.bat" (
call "%%~dpnG.bat" %2 %3
) else (
echo can't found script; failed call "%%~dpnG.bat" %2 %3
)
)
Output (shows responsivity to used delimiters):
==> 33955749b "first script,second,third" name address
first script.bat parameters: %*=name address
second.bat parameters: %*=name address
can't found script; failed call "D:\bat\SO\third.bat" name address
==> 33955749b "first script, second,third" name address
first script.bat parameters: %*=name address
can't found script; failed call "D:\bat\SO\ second.bat" name address
can't found script; failed call "D:\bat\SO\third.bat" name address
Note that both 33955749.bat and 33955749b.bat scripts
accept (partially or fully qualified) paths to called scripts, even with space(s);
on the other hand, they both ignore file extension(s) even if supplied and force .bat.
For instance, 33955749 name address first.cmd second.cmd would attempt to call first.bat and second.bat.
Resources (required reading, incomplete):
(command reference) An A-Z Index of the Windows CMD command line
(additional particularities) Windows CMD Shell Command Line Syntax
(%~G, %~1 etc. special page) Command Line arguments (Parameters)
(%variable:StrToFind=NewStr% etc.) Variable Edit/Replace

Check if process returns 0 with batch file

I want to start a process with a batch file and if it returns nonzero, do something else. I need the correct syntax for that.
Something like this:
::x.bat
#set RetCode=My.exe
#if %retcode% is nonzero
handleError.exe
As a bonus, you may consider answering the following questions, please :)
How to write a compound statement with if?
If the application My.exe fails to start because some DLL is missing will my if work? If not, how can I detect that My.exe failed to start?
ERRORLEVEL will contain the return code of the last command. Sadly you can only check >= for it.
Note specifically this line in the MSDN documentation for the If statement:
errorlevel Number
Specifies a true
condition only if the previous program
run by Cmd.exe returned an exit code
equal to or greater than Number.
So to check for 0 you need to think outside the box:
IF ERRORLEVEL 1 GOTO errorHandling
REM no error here, errolevel == 0
:errorHandling
Or if you want to code error handling first:
IF NOT ERRORLEVEL 1 GOTO no_error
REM errorhandling, errorlevel >= 1
:no_error
Further information about BAT programming: http://www.ericphelps.com/batch/
Or more specific for Windows cmd: MSDN using batch files
How to write a compound statement with
if?
You can write a compound statement in an if block using parenthesis. The first parenthesis must come on the line with the if and the second on a line by itself.
if %ERRORLEVEL% == 0 (
echo ErrorLevel is zero
echo A second statement
) else if %ERRORLEVEL% == 1 (
echo ErrorLevel is one
echo A second statement
) else (
echo ErrorLevel is > 1
echo A second statement
)
This is not exactly the answer to the question, but I end up here every time I want to find out how to get my batch file to exit with and error code when a process returns an nonzero code.
So here is the answer to that:
if %ERRORLEVEL% NEQ 0 exit %ERRORLEVEL%
The project I'm working on, we do something like this. We use the errorlevel keyword so it kind of looks like:
call myExe.exe
if errorlevel 1 (
goto build_fail
)
That seems to work for us. Note that you can put in multiple commands in the parens like an echo or whatever. Also note that build_fail is defined as:
:build_fail
echo ********** BUILD FAILURE **********
exit /b 1
To check whether a process/command returned 0 or not, use the operators && == 0 or not == 0 ||:
Just add operator to your script:
execute_command && (
echo\Return 0, with no execution error
) || (
echo\Return non 0, something went wrong
)
command && echo\Return 0 || echo\Return non 0
For details on Operators' behavior see: Conditional Execution || && ...
You can use below command to check if it returns 0 or 1 :
In below example, I am checking for the string in the one particular file which will give you 1 if that particular word "Error" is not present in the file and if present then 0
find /i "| ERROR1 |" C:\myfile.txt
echo %errorlevel%
if %errorlevel% equ 1 goto notfound
goto found
:notfound
exit 1
:found
echo we found the text.

assigning an alphanumeric value to a variable in batch

I want to assign the alpha numeric value to one variable in Batch scripting.
I tried following one but getting error.
setlocal
set test = \765514e2aad02ca658cc56cdb7884947 *E:\\test1
echo %test%
endlocal
Error:
C:\Users\bgannu>setlocal
C:\Users\bgannu>set test = \765514e2aad02ca658cc56cdb7884947 *E:\\test1
C:\Users\bgannu>echo 0
0
C:\Users\bgannu>endlocal
The syntax for set is set [[/a [expression]] [/p [variable=]] string]
The = has to be directly after your variable so you need to change:
set test = \765514e2aad02ca658cc56cdb7884947 *E:\\test1
to:
set test=\765514e2aad02ca658cc56cdb7884947 *E:\\test1
Otherwise your variable name would have a space at the end. You can easily try this out:
> set bar = foo
> echo %bar%
%bar%
> echo %bar %
foo
Note that both the variable name and its content got a space.
Lose the /A. the /A is used for arithmetic.
C:\test>set var=\765514e2aad02ca658cc56cdb7884947 *E:\\test1
C:\test>echo %var%
\765514e2aad02ca658cc56cdb7884947 *E:\\test1

Resources