Joshua's Docs - Batchfile / bat / Windows CMD - Cheatsheet
Light

Word of advice: When PATH doesn't seem to be working (recent updates not showing) there is no harm in restarting, and it often fixes the issue.

Resources

What & Link Type
SS64: "A-Z" CMD Reference Docs
SS64: Escape Syntax Cheatsheet

How to comment?

@REM # My Comment

Or, if you want the comments to show up in execution, use it without the @:

REM # My Comment

Using variables

SET MYPATH="C:\Users\Joshua"
cd %MYPATH%

WARNING: Be careful about wrapping variables in quotes; you can end up accidentally nesting quotes / double quoting.

Global Variables / Macros

Variable Example What
%path% C:\...\progA\bin;C:\...\progB;... Your global path variable
%cd% C:\Users\Joshua The current directory (of the caller, not script location!)
%~dp0 C:\... The full path of the residing script file
%ProgramFiles% C:\Program Files Main program files directory
%ProgramFiles(x86)% C:\Program Files (x86) Main x86 program files directory
%date% Wed 09/02/2020 Current date
%time% 12:50:00.86 Current time

This page has a comprehensive list of standard global environmental variables, and their default values. Also, lots of relevant tips for FOR variable syntax here.

Environmental Variables

Setting Environmental Variables

Use either the set command (temporary) or setx command (persisted).

Cmd:

@REM // Set User (local) variable - persists beyond current sessions
setx MYPATH "C:\Users\Joshua"

@REM // System variable (global) - requires elevated priv - persists beyond current sessions
setx MYPATH "C:\Users\Joshua" /M

@REM // Temporary, takes effect in current session, does not persist
set MYPATH="C:\Users\Joshua"

🚨 Notice that set has a different syntax than setx.

🚨 👉 SETX edits will only take effect in new console sessions, not the current one. See section below on refreshing environment variables.

"set" vs "setx"? Set is temporary - variable is set for duration of console session, whereas setx persists the value, even after you close the console.

Powershell:

$env:MYPATH="C:\Users\Joshua"

Using Environmental Variables

Same syntax as using variables - %ENV_VAR_NAME%.

Example: cd %APPDATA%

🚨 👉 Environment variables, by default, cannot be used in a console that was spawned before their values were set. See below.

Refreshing Environment Variables

Environment variables, by default, cannot be read back in a console if they were set after the console was launched. E.g., if you launch two consoles and then call setx in one to set a value, neither of the two consoles can read back the value that was just set, and you would have to either restart each one to read the value, or spawn a new console (which is the same thing).

This a serious shortcoming of how environment variables work on Windows, but there are some workaround scripts, many good ones in the answers to this StackOverflow question.

Sadly, the best solution in many cases still, is to simply grab a fresh console.

In ConEmu / Cmder, you can save the pain of restarting the entire Cmder instance by using Menu -> Active Console -> Restart or duplicate -> Restart [...]. Or, you can even create a hotkey for the action (under hotkeys, it is listed as: Recreate active console)

Clearing Environment Variables

If the value was set via SET instead of SETX, then you could just spawn a new terminal instance to be in an environment with fresh value. If you want to clear a value set via setx, and/or don't want to spawn a new terminal, the options to delete an environmental variable depend on what you want:

  • Deleting a variable for the current session:

    • Use set with an empty assignment (no trailing space): set MYVAR=
    • This will work for values set via SET, but will only work temporarily (current session) for those set via SETX
  • Deleting a persisted value (user or system)

    • User: REG delete HKCU\Environment /F /V MYVAR
    • System: REG delete "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /F /V MYVAR
    • Kudos to this SO response

Capturing Output to Variable

Kind of complicated in Windows...

The popular "trick" seems to be to use:

for /f %%i in ('MY_COMMAND_THAT_PRODUCES_OUTPUT') do set VARIABLE_NAME_TO_CAPTURE_TO=%%i

For details see:

Using the Clipboard in Windows Commands / Batch

Copying to the Clipboard

You can use CLIP to copy anything to the clipboard. For example,

echo "Hello" | CLIP

For more details, see this c3scripts.com tutorial page.

Unicode Issues with CLIP

The windows terminal has notorious issues with codepoints above the standard ascii range (e.g. emoji).

The fix for CLIP is the same for as using Unicode with the command prompt in general; change the code page using chcp 65001.

There is a PR I found that is a great illustrative solution - node-copy-and-paste PR #45.

Reading from the Clipboard

Unfortunately, there is no built-in command for reading from the clipboard in Windows, at least in command prompt. There is a tool built-in to PowerShell though; Get-Clipboard.

If you really need this functionality in CMD, you can always execute the Powershell command from command prompt:

powershell -command "Get-Clipboard"
REM # Can pipe or redirect as you please
REM # Example: print clipboard to stdout
powershell -command "Get-Clipboard" | cat

Strings

Joining string vars

Same as bash, you can just put them together:

SET strA=Hello
SET strB=World
SET final=%strA% %strB%
echo %final%

Hello World

And you can also put variables inside strings:

SET strA=Hello
echo "%strA% World"

Hello World

Removing Quotes from String Variables

To remove quotes from a variable, use set VAR_NAME=%VAR_NAME:"=% (ref)

Or, without re-assignment: echo %VAR_NAME:"=%

You can also avoid quoted variables by enclosing the entire set command in quotes when declaring it:

SET "UNQUOTED_STR=C:\Program Files"

Removing Line Breaks from Strings

Removing Line Breaks with String Manipulation Syntax

Similar to the above approach for removing quotes, you can use the string manipulation syntax to replace line breaks with nothing, thus removing them.

echo %VAR_NAME:~0,-1%

Credit to this S/O

However this doesn't remove the trailing line break that happens with echo Hello World > out.txt. See below

Removing Trailing Line Breaks and Empty Lines - Set

Removing a trailing empty line is, like many things in CMD, not as simple as many of us would like.

If you are looking for completely native option and have not yet saved or captured the output, the agreed upon method seems to be to use set /p (credit to S/O):

echo | set /p dummyName=Hello World>out.txt

@REM # This even works with variables
echo | set /p dummyName=%MY_VAR%>out.txt

Purposefully Including Line Breaks in Strings

Including line breaks in strings and variables is a little complicated with cmd / bat. There are multiple approaches, listed below in detail. For a good summary, this is a great S/O response.

Including Line Breaks - Using Echo:

If you don't need to store the string or pass it around, you can simply add more empty echo commands (echo;) to create line breaks. E.g:

echo Line One
echo;
echo Line Two

Notice how echo; is used instead of echo. - this is because the latter will attempt to search for files with the same name, thus is much slower.

Including Line Breaks - Macro approach:

@REM # The below line is actually a macro
SET NL=^& echo;
@REM # Notice how the below line does not use start & end quotes, 
@REM # but rather escaping quotes around the entire assignment block
SET "MULTI_LINE_STR=Line One%NL%Line Two"
echo %MULTI_LINE_STR%

An issue with the above approach is that, because it is a macro that calls echo and not actual characters, you can't really use it in a way that captures the output. For example, echo %MULTI_LINE_STR% > out.txt will only save the second line, because the macro gets expanded to where the actual executed code is more like:

echo Line One & echo:Line Two > out.txt

Including Line Breaks - Stored Variable with Delayed Expansion

@ECHO OFF
@REM # This is required for delayed expansion - makes escaping / using special chars easier
setlocal EnableDelayedExpansion enableextensions

@REM # Special newline var
(set NL=^
%=Do not remove this line=%
)

@REM # Notice use of wrapping quotes for escaping, plus expansion !
SET "MULTI_LINE_STR=Line One!NL!Line Two!"

@REM # Use !{VAR}! to expand
echo:!MULTI_LINE_STR!

@REM # Even redirecting to a file works!
@REM # As long as we expand with !{VAR}!
echo:!MULTI_LINE_STR! > out.txt

Above code based on this and this.

Including Line Breaks - Complex Escape Sequence

Ouch.

How to get system and user vars

  • Get path

    • PATH
    • More readable version

  • Getting user variables

    • can be accessed just by using %varName% syntax
    • echo %JAVA_HOME%

Tasklist

  • Find process by exe name

    • tasklist /FI "IMAGENAME eq Greenshot.exe"
  • Kill process by exe name

    • taskkill /IM {EXE_NAME}

      • Basically same thing as above: tasklist /FI "IMAGENAME eq {EXE_NAME}"
    • Optional: /F for "force"

Output text to file

  • Redirect operator

    • ipconfig /all > output.txt
  • Double redirect for append mode

    • ipconfig /all >> existingOutput.txt

Find executable paths

Use where to find files, as well as global executable / binary paths.

For example, where npm to find which NPM executables are globally registered in the path.

This is roughly equivalent to Unix's / bash's which command.

File Management

Copy files

There are a bunch of built-in tools. In order of online recommendations:

  • Robocopy

    • ss64
    • Basic syntax: ROBOCOPY {src} {dest} {?fileOrGlobPatterns} {?options}
    • Example: ROBOCOPY %cd% "C:\Users\Joshua\Dropbox\web-project" *.json *.html *.js *.md
    • This S/O covers the defaults for which files Robocopy will skip copying; you can use /IT to included tweaked files, and /IS to include same files
  • XCopy
  • Copy

List all files by size (recursive subdir too)

(@For /F "Delims=" %A in ('dir /B/S/A-D') Do @Echo %~fA %~zA) >filelist.txt

List file tree

  • For both files and folders

    • tree /F
  • Pipe to file - use /A for ascii output else file will be garbled

    • tree /F /A > filestructure.txt

Delete files of a certain type within current folder and all subfolders

Batch:

DEL /S *.docx

REMINDER - even though there is no wildcard at the ends, it still acts like one for some reason. So DEL /S *.qml will delete all .qml files, but also all .qmlc files - see https://stackoverflow.com/a/32399927

Powershell:

Remove-Item * -Include *.qml -Recurse | Remove-Item

NOTE - Powershell does not suffer from the above issue - .qml will match .qml and not .qmlc

Verifying Checksums

A built in utility that you can use is CertUtil.

For example:

CertUtil -hashfile my_program.exe SHA256

If you are looking for a GUI approach, check out hashcheck. Or this list of Windows freeware hash programs.

Since CertUtil can only hash files (not piped or stored strings), to check an online file (via cURL), clipboard contents, or any other strings, you would need to temporary save to a file and then pass the file to CertUtil.

curl https://.../myfile.txt > temp
CertUtil -hashfile temp SHA256
DEL temp

🚨 WARNING: Be really wary of how easy it is to accidentally add or include newlines and/or extra spacing in the content you are trying to generate a hash from. If you accidentally add one in the shell that is not present in the content, the hashes won't match.

If you want to quickly hash / encode strings with a GUI, I highly recommend the CyberChef online tool. Here is an example recipe for SHA256+Base64.

Converting Paths to Forward Slashed

If you have git-bash, or something like that which includes the sed program, that is a fast solution:

echo '\\dir\\file.txt' | sed -E 's/\\\\/\//g'
# /dir/file.txt

Logic

IF / Else

The basics:

If (condition) (do_something) ELSE (do_something_else)

Make sure logic switches are on same line - https://ss64.com/nt/else.html

This Won't work:

IF EXIST %MyFile% (
	echo "found"
)
ELSE (
	echo "not found"
)

This should work:

IF EXIST %MyFile% (
	echo "found"
) ELSE (
	echo "not found"
)

ELSE IF

Else If can be problematic with .bat batch scripts. Might work in CMD, but for BAT, seems most people recommend nested IFs as alternative, or GOTOs. Worked fine for me though, with Win 8

Using the current directory you are in as a variable

  • Simple!

    • echo %cd%
  • Works great for opening current folder with various programs

    • "C:\Program Files\Microsoft VS Code\Code.exe" %cd%
    • Explorer.exe %cd%

Keeping file open after execution

Add this to very end of file:

PAUSE

Input

Automatically receiving calling input

In a .bat file, you can automatically receive and use the input that was provided when the file was called by using %1, %2, and so on.

Asking for input

You can pause your script and ask for input by using this:

set /P {variableName}="Prompt String"
REM Access by using %{variableName}%

Example:

set /P name="What is your name? "
echo Good day, %name%!
PAUSE

Passing Input Arguments to Another Program

If you need to pass trailing input arguments to another program, you can use %*.

For example, if I wanted to create a stub file in a \bin folder that simply calls another, I could do something like this:

%~dp0..\src\my-bin.exe %*

By the way, this is how NodeJS handles cmd files in the \.bin folder - they use this syntax to call node your-file.js your-args.


File permissions

You can use TAKEOWN to take ownership of files.

Example:

REM # Folder contents is foo.txt and subfolder ./pics.
REM # We need to take ownership of ./pics
takeown /r /f pics

Symlinking

On Win, you can use MKLink (ss64) to create a symlink to a directory or file.

  • Link to a file:

    • MKlink {symbolicFileName} {realTargetFile}
  • Link to a directory / folder

    • MKlink /D {symbolicDirName} {realTargetDir}

Creating a symlink requires an elevated (with admin rights) console. (Unless using dev mode on Win 10+)

In general, it is best / easiest to always use absolute paths for the targets. You might want to wrap paths in quotes if you are having issues.

If you want to check what a symlink maps to (or just see if there are any in the current dir), you can use:

dir /a, or for just links, dir /aL

Creating Windows Shortcuts with Batch

Windows shortcut files (.lnk) are a very special proprietary file type, completely separate from symlinks. Their proprietary nature, and the fact that many programs open the target instead of the .lnk file itself, make them difficult to both edit and create.

There is essentially no built-in way to easily create shortcut files from the command line in Windows, especially in Win10. You can work around this with third-party utility programs, VB Scripts, or PowerShell.

My recommendation would be to use NirSoft's NirCmd tool, and its shortcut command.

Networking

Active Connections, Ports, Etc

The go-to command for listing active connections and listening ports is netstat.

To find info on a specific port, you could use something like netstat -bnao | grep "8080" (or find, if lacking git-bash). You can then go a step further, and take a PID from that output and use tasklist /fi "pid eq PID_from_netstat" to get process info. Or use taskkill /F /PID PID_from_netstat to kill the process.

Markdown Source Last Updated:
Wed Jun 09 2021 00:15:46 GMT+0000 (Coordinated Universal Time)
Markdown Source Created:
Mon Aug 19 2019 17:06:24 GMT+0000 (Coordinated Universal Time)
© 2021 Joshua Tzucker, Built with Gatsby
Feedback