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 | Type | Link |
---|---|---|
SS64 CMD Reference | Docs | A-Z CMD Index |
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 |
%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.
Environmental Variables
Setting Environmental Variables
Cmd:
@REM // Set User (local) variable
setx MYPATH "C:\Users\Joshua"
@REM // System variable (global) - requires elevated priv
setx MYPATH "C:\Users\Joshua" /M
🚨 👉
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
)
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"
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%
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"
-
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:
-
- 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 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
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
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.