A colleague of mine is a Linux hacker who took a job in Seattle and has been thrust into Windows. On his team, PowerShell is not always available, so he's left saying "look dude, I know bash; what the heck do I do with this??" If you're in a similar situation, this is a primer for you.
Below are a few bash-isms and Unix-isms, and their cmd.exe analogues, as well as some things that are purely from CMD. To try them, open up
cmd.exe (Start > Run:
cmd.exe, or Windows+R:
cmd.exe) and go to town.
One-Liners
I'll start with some one-liners. The
$ prompt indicates commands that can be used in bash on GNU/Linux and similar operating systems, and the
> prompt represents the cmd.exe equivalent. Omit the prompts ($ and >) when trying these commands.
Run something else
$ bash -c something else
> cmd /c something else
> cmd /k something else
In the latter command, cmd will stay resident and permit further commands.
Display program return value:
$ echo $?
> echo %ERRORLEVEL%
Some programs, when they are run, return a numeric status code. Generally, 0 indicates success, and 1 or greater indicates an error. Windows executables that display graphical windows (
calc.exe and
winword.exe are examples) return 0 immediately and unconditionally, and run "asynchronously" -- meaning, the command prompt receives the return value immediately and allows the user to type more commands.
Compare program return value:
$ if [ $? -eq 0 ]; then echo Success; fi;
> if %errorlevel% == 0 echo Success
Find program in path:
$ which which
> where where
Find file:
$ find / -type f -name whatever\*.txt
> dir /s \whatever*.txt
> dir /a/b/s \whatever*.txt
The latter command will show all files, even those having the hidden attribute (
/a) and will provide bare output without any file sizes or other details (
/b).
Find string in files:
$ grep -Ri needle *
> findstr /S /I needle *
Find string in program output:
$ ifconfig | grep 192
> ipconfig | findstr 192
Start service (e.g. mysql):
$ service mysql start
> sc start mysql
> net start mysql
The latter command (
net.exe) can do many things including viewing and modifying local groups, authenticating to network shares and mapping them to drive letters, etc. The other command (
sc.exe) is strictly for starting and stopping services, viewing their configuration, and other tasks.
Restart web server:
$ apachectl -k restart > /dev/null 2>&1
> iisreset > nul 2>&1
Terminate by pid:
$ kill -s 9 916
> taskkill /f /PID 916
Terminate by name:
$ killall -s 9 kcalc
> taskkill /f /IM calc.exe
Shut down:
$ shutdown -h now
> shutdown /s /t 0
Reboot:
$ shutdown -r now
$ reboot
> shutdown /r /t 0
Add user to group:
$ useradd -G root mike
> net localgroup administrators /add mike
Set environment variable:
$ variable=hello
> set variable=hello
Display environment variable:
$ echo $variable
> echo %variable%
Prompt for environment variable:
$ read -p "Type something: " variable
$ echo $variable
> set /p variable=Type something:
> echo %variable%
Pre-set variables such as username:
$ echo $USER
> echo %username%
Dump environment:
$ setenv
> set
The
SET command also accepts partial variable names, and will list all the variables and values whose names match that string.
Compare files:
$ cmp file1 file2
> fc file1 file2
You can check the errorlevel (the numeric error or success code returned by the program) to determine whether the files are the same. Identical files result in a 0 errorlevel, and differing files result in a return value of 1 or greater.
Display file contents:
$ cat file
> type file
> more file
The latter command,
more, can be used to display the contents of those notorious
alternate data streams, and also serves as a pager (see next).
Display file contents with pager:
$ less file
$ cat file | less
> more file
> type file | more
Loops:
$ for ((n=2; n<=8; n+=2)); do echo $n; done
> for /l %n in (2, 2, 8) do echo %n
Operate on a set of arbitrary words:
> for word in hello there; do echo $word; done
$ for %w in (hello there) do ( echo %w )
Echo the names of all text files in the current dir:
> for file in *.txt; do echo $file; done
$ for %f in (*.txt) do echo %f
Echo the names of all text files recursively:
$ for file in $(find . -name \*.txt); do echo $file; done
> for /f "usebackq" %f in (`dir /a/b/s *.txt`) do echo %f
> for /r %f in (*.txt) do echo %f
Parse IP addresses out of IP configuration:
$ echo IP: $(ifconfig | grep 'inet addr' | awk -F: '{print $2}' | awk '{print $1}')
>for /f "usebackq delims=: tokens=1,2" %a in (`ipconfig ^| findstr /i IPv4`) do echo IP: %b
Scripts
Read file, find pattern, copy to another location
cprintf.sh:
#!/bin/bash
dstdir=~/cfiles;
rm -rf $dstdir;
mkdir $dstdir;
for file in $(find . -type f -name \*.c); do
grep -i printf $file > /dev/null 2>&1;
if [ $? -eq 0 ]; then
cp $file $dstdir;
fi;
done;
cprintf.cmd:
@echo off
set dstdir=%userprofile%\cfiles
if exist "%dstdir%" rmdir /s /q "%dstdir%"
mkdir "%dstdir%"
for /r %%f in (*.c) do (
findstr /i printf %%f > nul 2>&1
if not errorlevel 1 copy %%f "%dstdir%" > nul 2>&1
)
Because of the idiosynchrasies of the Windows command interpreter and native utilities, writing robust scripts for CMD is akin to any of the following activities:
- Leveling and hanging a picture in an earthquake
- Making a bed with a rabid dog in it
- Asking a room full of four-year-olds to each draw a triangle
- Wrestling with a snake, a crab, and an orangutan at the same time
- Balancing a system of simultaneous equations by inspection while inebriated
Here are the idiosynchrasies that are relevant to the above script:
Double percent signs (e.g.
%%f) are used to denote loop variables in .cmd and .bat files instead of single percent signs (e.g.
%f) as on the command line. Variable names must be a single character in length (e.g.
%a on the command line, and
%%a in a script file).
Also, evaluation of errorlevels can be done both by comparison with the
%ERRORLEVEL% environment variable and using the
IF [NOT] ERRORLEVEL construct. The help for the if command (accessible by typing IF /?) states:
ERRORLEVEL number Specifies a true condition if the last program run
returned an exit code equal to or greater than the number
specified.
And:
NOT Specifies that Windows should carry out
the command only if the condition is false.
Just so you've got that straight: don't ask the command interpreter this:
IF ERRORLEVEL 0 ECHO Oh yes, everything is fine <-- NO, IT IS
NOT
There are some more turds in the punch bowl...
If you mean to set variables in a for loop and reflect on those values later in the script, you must first invoke
SETLOCAL ENABLEDELAYEDEXPANSION. To obtain the most up-to-date value of each variable, you must then use exclamation points, not percent signs, to access the data. For example:
!frick!.
If you use a pipe within a backtick expression in a
FOR /F "usebackq" statement, escape the pipe with the caret symbol. For example,
`dir /a/b/s *.txt ^| findstr x`.
Windows scripts access arguments as
%1,
%2, etc. Tilde modifiers such as
%~n0 (equivalent to basename $1) are used to parse filenames, and can be found in the help for the
FOR command.
Environment variables inherently support substring selection and pattern replacement:
C:\Users\mykill>echo %username:kill=ke%
myke
C:\Users\mykill>echo %username:~0,3%
myk
I will probably update this article to include more info. Suggestions are welcome.
For more loopy help:
for /?
The help from a few other commands can also be informative:
if /?
setlocal /?