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.
If you are trying to use a path directly to access contents (for example, with
in (filename.txt)
, you won't want quotes around the string)
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 thansetx
.
🚨 👉
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 viaSETX
- Use
- Deleting a persisted value (
user
orsystem
)- 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
- User:
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:
- S/O:
- Microsoft - The Old New Thing blog post
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 ofecho.
- 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
Including Line Breaks - Complex Escape Sequence
Ouch.
How to get system and user vars
- Get path
PATH
- More readable version
echo %path:;=&echo.%
- Getting user variables
- can be accessed just by using %varName% syntax
echo %JAVA_HOME%
Output text to file
- Redirect operator
ipconfig /all > output.txt
- Double redirect for append mode
ipconfig /all >> existingOutput.txt
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 - Use
/l
to perform a dry-run / preview what would happen
- 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 garbledtree /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
Waiting for a process to finish in a batch script
Usually using the call
command is the most reliable way to start a process or script and wait for it to finish:
CALL my-prog
Another option that sometimes works is using the start
command, specifically with wait
START /wait my-prog
💡 StackOverflow:
CALL
vsSTART /WAIT
Error Handling
There are multiple ways to handle errors in batch scripts.
@REM # With redirection
@Echo off
SomeCommand && (
Echo success
) || (
Echo failed/error
)
@REM # With explicit error code checking:
if !errorlevel! neq 0 exit /b !errorlevel!
If you want to run something, batch scripts included, and capture both stdout and stderror to a file, you can use
mycommand > output.txt 2>&1
, which is a very common redirection trick
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 callnode your-file.js your-args
.
For more details, see SS64 - Pass Command Line Arguments to a Windows Batch File
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.
Evaluating symlinks
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.
Applications, Binaries, Launching, and Killing
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.
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}"
- Basically same thing as above:
- Optional:
/F
for "force"
Launching Applications
Command | What? |
---|---|
open "" "https://joshuatz.com" |
Open a URL with the default web browser |
Handy Commands for Exploring an OS
Command | What? |
---|---|
systeminfo |
Displays a bunch of system information, including OS version, PC model, uptime, and more. |