All notes
Msdos

Basics

File extensions

Batch files are interpreted by the Command Prompt (cmd.exe).

The script is kept inside a batch file, with the extension .bat or .cmd.

Although .bat is more recognisable, as it was used in the MS-DOS environment that preceded the Command Prompt, the Command Prompt's interpretations of batch files is very different to the manner of interpreting DOS batch files, and .cmd files are only interpreted by the Command Prompt, so using the .cmd extension prevents mis-use in older environments.

To invoke the bat file:


call test.bat

Init script for MSDOS

stackOverflow: aliases in CMD.

Create a .bat or .cmd file with your DOSKEY commands.
Run regedit and go to HKEY_CURRENT_USER -> Software -> Microsoft -> Command Processor
Add String Value entry with the name AutoRun and the full path of your .bat/.cmd file.

For example, %USERPROFILE%\alias.cmd
Replacing the initial segment of the path with %USERPROFILE% is useful for syncing among multiple machines.

@echo off

:: Temporary system path at cmd startup

set PATH=%PATH%;"C:\Program Files\Sublime Text 2\"

:: Add to path by command

DOSKEY add_python26=set PATH=%PATH%;"C:\Python26\"
DOSKEY add_python33=set PATH=%PATH%;"C:\Python33\"

:: Commands

DOSKEY ls=dir /B
DOSKEY sublime=sublime_text $*  
    ::sublime_text.exe is name of the executable. By adding a temporary entry to system path, we don't have to write the whole directory anymore.
DOSKEY gsp="C:\Program Files (x86)\Sketchpad5\GSP505en.exe"
DOSKEY alias=notepad %USERPROFILE%\Dropbox\alias.cmd

:: Common directories

DOSKEY dropbox=cd "%USERPROFILE%\Dropbox\$*"
DOSKEY research=cd %USERPROFILE%\Dropbox\Research\

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Command Processor]
"CompletionChar"=dword:00000009
"DefaultColor"=dword:00000000
"EnableExtensions"=dword:00000001
"PathCompletionChar"=dword:00000009
"Autorun"="\"D:\\proGreen\\clink\\0.4.7\\clink.bat\" inject --autorun --profile ~\\clink ; %USERPROFILE%\\wcfCMD.bat"

Language References

Comments


REM Comment here.

::Comment here.

command & ::Comment here.

"::" is prefered, for being faster to execute and write, and also for being easy to differentiate from normal commands.

Variables


SET name=John Smith
:: "John Smith"

:: /A means arithmetic:
SET /A number=38
%cd%    CWD.
%~dp0%  The full path to the bat file's CWD.

:Func
  echo params: "%~0" "%~1" "%~2"
  echo calling file: "%~nx0"
  echo full: "%~dpnx0%0"
goto :EOF

EnableDelayedExpansion

ss64.com: Delayed Expansion.

A typical example:


Setlocal EnableDelayedExpansion
Set _var=first
Set _var=second& Echo %_var% !_var!
:: Output: first second

Explanation. Condisider the last line in processing,

  1. The last line is first parsed and the variable %_var% is expanded to "first".
  2. Then the line is executed (changing the value of _var).
  3. Finally !_var! is expanded (because it is using delayed expansion) and it now shows the updated value "second".

Another example:


@echo off
Setlocal
:: escape characters (^)
Set _html=Hello^>World
Echo %_html%

:: In the above, the Echo command will create a text file called 'world'.
:: This is because the variable is expanded at parse time, so the last line is executing "Echo Hello > World" and the > character is interpreted as a redirection operator.

:: If we now try the same thing with EnableDelayedExpansion:
Setlocal EnableDelayedExpansion
Set _html=Hello^>World
Echo !_html!
:: Output: Hello>World

:: Without escaping
Set _html=Hello>World
Echo !_html!
:: Output: Hello

Used in for loops:


@echo off

setlocal
set _tst=0
FOR /l %%G in (1,1,5) Do (echo [%_tst%] & set /a _tst+=1)
echo Total = %_tst%
:: [0]
:: [0]
:: [0]
:: [0]
:: [0]
:: Total = 5

:: When the FOR loop finishes we get the correct total, so the variable correctly increments, but during each iteration of the loop the variable is stuck at it's initial value of 0.

setlocal EnableDelayedExpansion 
set _tst=0
FOR /l %%G in (1,1,5) Do (echo [!_tst!] & set /a _tst+=1)
echo Total = %_tst%
:: [0]
:: [1]
:: [2]
:: [3]
:: [4]
:: Total = 5

@echo off
Setlocal EnableDelayedExpansion
Set var1=Hello ABC how are you
Set var2=ABC
Set result=!var1:%var2%=Beautiful!
Echo !result!
:: Output: Hello Beautiful how are you

Functions

dostips.com: functions.

Define a funtion

In DOS you write a function by surrounding a group of command by a label and a GOTO:EOF command. The label becomes the function name.


:myDosFunc - here starts my function identified by it`s label
  echo. here the myDosFunc function is executing a group of commands
  echo. it could do a lot of things
GOTO:EOF

Arguments

Just as the DOS batch file itself can have arguments, a function can be called with arguments in a similar way. Just list all arguments after the function name in the call command.

Use a space or a comma to separate arguments.

Use double quotes for string arguments with spaces.

Function with Arguments:


echo.Going to execute myDosFunc with different arguments
call:myDosFunc 100 YeePEE
call:myDosFunc 100 "for me"
call:myDosFunc 100,"for me"
call:myDosFunc 100,for me

:: End script and pause.
echo.&pause&goto:eof

:myDosFunc
  echo. it could do %~1 of things %~2.
goto:eof

:: Output
:: Going to execute myDosFunc with different arguments
::  it could do 100 of things YeePEE.
::  it could do 100 of things for me.
::  it could do 100 of things for me.
::  it could do 100 of things for.

Just as the DOS batch file itself retrieves arguments via %1 : %9 a function can parse it`s arguments the same way. The same rules apply.

To strip of the double quotes in an arguments value, use the tilde modifier, i.e. use %~2 instead of %2.


call:myDosFunc "Good."
goto:eof

:myDosFunc
  echo. %~1, %1.
goto:eof

:: Good., "Good.".

Returning Values

Classic: Using global variable

The CALL command doesn`t support return values as known by other programming languages.

The classic walkaround is to have the function store the return value into a environment variable. The calling script can use this variable when the function returns.


set "var1=some hopefully not important string"
echo.var1 before: %var1%
call:myGetFunc
echo.var1 after : %var1%

:myGetFunc    - get a value
  set "var1=DosTips"
goto:eof

Using Variable Reference


set "var1=CmdTips"
echo.var1 before: %var1%
call:myGetFunc var1
echo.var1 after : %var1%

echo.&pause&goto:eof

:myGetFunc  - passing a variable by reference
set "%~1=DosTips"
goto:eof

:: Within the :myGetFunc function the command processor works like this:
:: 1. Reads the set command into memory: set "%~1=DosTips"
:: 1. Expand the variables, i.e. %~1 like this: set "var1=DosTips"
:: 1. Finally execute the command and assign the new string to var1

SetLocal, EndLocal

The SETLOCAL causes the command processor to backup all environment variables. The variables can be restored by calling ENDLOCAL. Changes made in between are local to the current batch.

ENDLOCAL is automatically being called when the end of the batch file is reached, i.e. by calling GOTO:EOF.


set "aStr=Expect no changed, even if used in function"
set "var1=No change for this one.  Now what?"
echo.aStr before: %aStr%
echo.var1 before: %var1%
call:myGetFunc var1
echo.aStr after : %aStr%
echo.var1 after : %var1%

echo.&pause&goto:eof

:myGetFunc    - passing a variable by reference
  SETLOCAL
  set "aStr=DosTips"
  set "%~1=%aStr%"
  ENDLOCAL
goto:eof

:: Output
:: aStr before: Expect no changed, even if used in function
:: var1 before: No change for this one.  Now what?
:: aStr after : Expect no changed, even if used in function
:: var1 after : No change for this one.  Now what?

How to pass return values over the ENDLOCAL barrier

How to return a value that was calculated before executing ENDLOCAL when ENDLOCAL restores all variables back to its original state?

The answer comes with "variable expansion". The command processor expands all variables of a command before executing the command. Letting the command processor executing ENDLOCAL and a SET command at once solves the problem. Commands can be grouped within brackets.


set "aStr=Expect no changed, even if used in function"
set "var1=Expect changed"
echo.aStr before: %aStr%
echo.var1 before: %var1%
call:myGetFunc var1
echo.aStr after : %aStr%
echo.var1 after : %var1%

echo.&pause&goto:eof

:myGetFunc    - passing a variable by reference
SETLOCAL
set "aStr=DosTips"
( ENDLOCAL
    set "%~1=%aStr%"
)
goto:eof

:myGetFunc2    - passing a variable by reference
SETLOCAL
set "aStr=DosTips"
ENDLOCAL&set "%~1=%aStr%"       &rem THIS ALSO WORKS FINE
goto:eof

:: Output
:: aStr before: Expect no changed, even if used in function
:: var1 before: Expect changed
:: aStr after : Expect no changed, even if used in function
:: var1 after : DosTips

Recursive function

Being able to completely encapsulate the body of a function by keeping variable changes local to the function and invisible to the caller, we are now able to call a function recursively making sure each level of recursion works with its own set of variables even thought variable names are being reused.

Fibonacci:


set "fst=0"
set "fib=1"
set "limit=10000"
call:myFibo fib,%fst%,%limit%
echo.The next Fibonacci number greater or equal %limit% is %fib%.

echo.&pause&goto:eof

:myFibo  -- calculate recursively the next Fibonacci number greater or equal to a limit
::       -- %~1: return variable reference and current Fibonacci number
::       -- %~2: previous value
::       -- %~3: limit
SETLOCAL
set /a "Number1=%~1"
set /a "Number2=%~2"
set /a "Limit=%~3"
set /a "NumberN=Number1 + Number2"
if /i %NumberN% LSS %Limit% call:myFibo NumberN,%Number1%,%Limit%
(ENDLOCAL
    IF "%~1" NEQ "" SET "%~1=%NumberN%"
)
goto:eof

:: Output:
:: The next Fibonacci number greater or equal 10000 is 10946.

set location=%cd%
echo "%location%"
:: Echos current working directory.

A standard DOS function template


:myFunctionName    -- function description here
::                 -- %~1: argument description here
SETLOCAL
REM.--function body here
set LocalVar1=...
set LocalVar2=...
(ENDLOCAL & REM -- RETURN VALUES
    IF "%~1" NEQ "" SET %~1=%LocalVar1%
    IF "%~2" NEQ "" SET %~2=%LocalVar2%
)
GOTO:EOF

For loop

ss64: for. <<<<<<< HEAD

FOR - Loop through a set of files in one folder
FOR /R - Loop through files (recurse subfolders)
FOR /D - Loop through several folders
FOR /L - Loop through a range of numbers
FOR /F - Loop through items in a text file
FOR /F - Loop through the output of a command
======= >>>>>>> refs/remotes/origin/master
---- Files and Directories

syntax-FOR-Files
    FOR %%parameter IN (set) DO command 
   
syntax-FOR-Files-Rooted at Path   
    FOR /R [[drive:]path] %%parameter IN (set) DO command 
  
syntax-FOR-Folders
    FOR /D %%parameter IN (folder_set) DO command

---- List of Numbers.
  
syntax-FOR-List of numbers   
      FOR /L %%parameter IN (start,step,end) DO command 
  
syntax-FOR-File contents   
    FOR /F ["options"] %%parameter IN (filenameset) DO command 
    FOR /F ["options"] %%parameter IN ("Text string to process") DO command
  
syntax-FOR-Command Results 
    FOR /F ["options"] %%parameter IN ('command to process') DO command

If you are using the FOR command at the command line rather than in a batch program, use just one percent sign: %G instead of %%G.

Format letters are case sensitive, so using a capital letter is also a good way to avoid conflicts %%A rather than %%a.

Examples


FOR /F "tokens=1-5" %%A IN ("This is a short sentence") DO @echo %%A %%B %%D
:: Output: This is short

EnableDelayedExpansion

The following example counts the files in the current folder, but %count% always returns 1:


@echo off
SET count=1
<<<<<<< HEAD
 FOR /f "tokens=*" %%G IN ('dir /b') DO (
 echo %count%:%%G
 set /a count+=1 )

FOR /F "tokens=4 delims=," %%G IN ("deposit,$4500,123.4,12-AUG-09") DO @echo Date paid %%G
:: Date paid 12-AUG-09

SO: loop through comma separated string in batch. Iterate through string list:


@echo off
set themes=Hot Sun,Hard Granite,Shimmering Bright Twilight
for %%a in ("%themes:,=" "%") do (
    echo %%~a
)
=======
FOR /f "tokens=*" %%G IN ('dir /b') DO (
    echo %count%:%%G
    set /a count+=1 )
>>>>>>> refs/remotes/origin/master

To update variables within each iteration of the loop we must either use EnableDelayedExpansion or else use the CALL :subroutine mechanism as shown below:


@echo off
SET count=1
FOR /f "tokens=*" %%G IN ('dir /b') DO (call :subroutine "%%G")
GOTO :eof

:subroutine
  echo %count%:%1
  set /a count+=1
  GOTO :eof

Loop through a string list

Split a string:


@echo off & setlocal

for %%a in (AAA BBB CCC DDD EEE FFF) do echo %%a
:: AAA
:: BBB
:: CCC
:: DDD
:: EEE
:: FFF

for %%a in ("AAA BBB CCC DDD EEE FFF") do echo %%a
:: "AAA BBB CCC DDD EEE FFF"

for %%a in ("AAA") do echo %%a
:: "AAA"

for %%a in ("AAA") do echo %%~a
:: AAA

@echo off
set themes=Sun,Granite,Twilight
for %%a in ("%themes:,=" "%") do (
   echo %%a
)
:: "Sun"
:: "Granite"
:: "Twilight"

echo ("%themes:,=" "%")
:: ("Sun" "Granite" "Twilight")

set themes=Hot Sun,Hard Granite,Shimmering Bright Twilight
for %%a in ("%themes:,=" "%") do (
    echo %%~a
)
:: Hot Sun
:: Hard Granite
:: Shimmering Bright Twilight

Double-quoted items are taken as single items, which means for %%a in ("one" "two" "three and four") do echo %%a will give the output "one\r\ntwo\r\nthree and four".

Commands

dir


/B: brief.
/O FLAG: order. Flag: N-name, S-size, E-extension, D-datetime, G-group, '-'-reverse.
/P: pause.
/S: show all files recursively.
/T FLAG: set time format. Flag: C-createTime, A-accessTime, W-writeTime.

Echo


@ECHO OFF
:: Firstly, always turn echo off.

:: The '.' is used to indicate the following are all msg to be printed:

ECHO.Hello World!

echo.on                           
:: on                                  
echo on                           
:: (nothing)                                    
echo .on                          
:: .on                                 

:: Remember to turn echo on. Not needed since WinXP.
ECHO ON

HINT: Starting with Windows XP, the ECHO ON command is optional. The command interpreter automatically enables it after the BAT file terminates.

Copy, Move, XCopy

MicroSoft: copy.

wcfNote: these three commands can only manipulate on one file each time. Use for loop to handle multiple files.

Common switches (available to all the three commands):

/Y    Suppresses prompting to confirm you want to overwrite an existing destination file.
/-Y   Causes prompting to confirm you want to overwrite an existing destination file.

The switch /Y may be present in the COPYCMD environment variable. This may be overridden with /-Y on the command line. Default is to prompt on overwrites unless COPY/XCOPY/MOVE command is being executed from within a batch script.


rem To move one or more files:
MOVE [/Y | /-Y] [drive:][path]filename1[,...] destination

rem To rename a directory:
MOVE [/Y | /-Y] [drive:][path]dirname1 dirname2

COPY [/V] [/N] [/Y | /-Y] [/Z] [/A | /B ] source [/A | /B]
     [+ source [/A | /B] [+ ...]] [destination [/A | /B]]
/A           Indicates an ASCII text file.
/B           Indicates a binary file.
rem To append files, specify a single file for destination, but multiple files for source (using wildcards or file1+file2+file3 format).

XCOPY source [destination] [/A | /M] [/D[:date]] [/P] [/S [/E]] [/V] [/W]
                           [/C] [/I] [/Q] [/F] [/L] [/H] [/R] [/T] [/U]
                           [/K] [/N] [/O] [/X] [/Y] [/-Y] [/Z]
/D:m-d-y     Copies files changed on or after the specified date.
               If no date is given, copies only those files whose
               source time is newer than the destination time.
/S           Copies directories and subdirectories except empty ones.
/E           Copies directories and subdirectories, including empty ones.
/C           Continues copying even if errors occur.
/Q           Does not display file names while copying.
/F           Displays full source and destination file names while copying.
/L           Displays files that would be copied.
/H           Copies hidden and system files also.
/R           Overwrites read-only files.
/T           Creates directory structure, but does not copy files. Does not
               include empty directories or subdirectories. /T /E includes
               empty directories and subdirectories.
/U           Copies only files that already exist in destination.
/O           Copies file ownership and ACL information.

// Good examples:

// Copy a set of DLL and PDB files from a set of folders recursively into another folder
// Use "%%x" in a batch file
// Use /Y to clobber any duplicate names.
mkdir targetDir
for /r %%x in (*.dll, *pdb) do copy "%%x" targetDir\ /Y

robocopy

techNet.


:: Copy directory a to directory b.
:: Overwrite: If there is a/aaa, and b is empty, then after robocopy, there is b/aaa too.
robocopy a b
:: The result is same:
robocopy a b\

:: Copy all including empty directories.
robocopy /E
:: Copy subdirectories but not empty ones.
robocopy /S

:: List only files larger than 32 MBytes(33553332 bytes) in size.
Robocopy.exe c:\sourceFolder d:\targetfolder /min:33553332 /l

md

SO: mkdir -p on windows.


@echo off
setlocal enableextensions
md %1
endlocal

Almost every one of the cmd scripts I write begins with:


setlocal enableextensions enabledelayedexpansion

to ensure I get as close as possible to the behavior of my beloved bash.

start

Superuser. Run a windows command line in background:


START /B program > somefile.txt 2>&1

FAQs

Find where msbuild.exe is


REM Run this bat file to set environments for msbuild.
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat"
REM where is the equivalent of "which" in Linux.
where msbuild.exe
REM C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe

Call multiple bat files in a single bat

When not using CALL, the current batch file stops and the called batch file starts executing. It's a peculiar behavior dating back to the early MS-DOS days.


call msbuild.bat
call unit-tests.bat
call deploy.bat