In my last article, I made the argument that you should stop writing batch files (Cmd.exe shell scripts) and instead focus your efforts on learning PowerShell. Of course, there is always a learning curve to new technologies, and Windows PowerShell is no exception. 

If you’re like me and you have a long history writing batch files, there are some things you’re going to have to unlearn, or relearn, to make the leap from batch to PowerShell. Cmd.exe uses a number of special characters, and many of these don’t have a PowerShell equivalent (or mean something entirely different in PowerShell). In this article, I’ll describe the special characters that Cmd.exe uses and how a number of these characters work differently in PowerShell.

The @ Character

By default, Cmd.exe displays each batch file command as it executes it (this is referred to as command echoing). You can disable command echoing for an individual command by using the @ character at the beginning of the command, and you can disable command echoing for all subsequent commands in your batch file by using the Echo Off command. 

Command echoing can be helpful when debugging a batch file, but most batch file authors add the @Echo Off command at the top of every batch file to disable command echoing (the @ at the beginning of the command prevents the command that disables echoing from itself being echoed). PowerShell does not echo commands like Cmd.exe, so there’s no need for the @Echo Off command in PowerShell. 

PowerShell uses the @ character to create arrays or hash tables. Cmd.exe doesn’t support either arrays or hash tables, but you can read about these useful data structures in the about_Arrays and the about_Hash_Tables PowerShell help topics.

The % Character

The % character has several special meanings to the Cmd.exe shell: It specifies environment variables and two kinds of parameter replacements. Let’s take a look at these. 

Environment variables. In Cmd.exe, you use the % character to surround the name of an environment variable, and the shell expands the variable to its value. For example, %SystemRoot% expands to C:\Windows. PowerShell does not use this syntax; instead, you need to write $Env:SystemRoot (i.e., $Env: followed by the environment variable’s name). 

For command parameter replacement. The For command uses a somewhat complex syntax that’s designed to repeat a command iterate a set of items (for example, strings, files, directories, etc.) and run a command for each resulting item. The % character, followed by a single letter a through z, is a parameter that Cmd.exe replaces with the current item. For example, in Cmd.exe, the command 

for %p in (A B) do echo %p

runs the Echo command twice: Once for the letter A and a second time for the letter B. (Note that you must double the % character if you use the For command in a batch file instead of typing it at the Cmd.exe prompt.) 

PowerShell, in comparison, has several different ways of iterating sets of items. The ForEach-Object cmdlet is the closest equivalent to the Cmd.exe For command. The above in PowerShell would look like the following: 

"A","B" | foreach-object { $_ }

$_ is the equivalent of %p in the Cmd.exe For command example above. $_ is a special variable that means “the current object from the pipeline,” which in this example gets replaced by each string object (“A”, then “B”). The quotes are required on the left side of the pipe (|) because otherwise PowerShell won’t know you’re referring to string objects. 

Note: PowerShell uses the % character as an alias (substitute command name) for the ForEach-Object cmdlet, so you could write the above PowerShell command as follows: 

"A","B" | % { $_ }

The % alias makes sense to Perl programmers, but it’s confusing to those who are accustomed to the various uses of % in batch files. I recommend spelling out the cmdlet name (ForEach-Object) instead to reduce confusion. 

Replaceable parameters. In a batch file, the % character followed by a single digit 0 through 9 is replaced by its corresponding command-line parameter: %0 is the batch file’s name, %1 is the first parameter on the batch file’s command line, %2 is the second parameter, and so forth. Figure 1 shows a sample batch file that uses a replaceable parameter. 

Figure 1 - Replaceable parameter in a batch file


Although a PowerShell script can access .parameters by position, it’s usually much better to use PowerShell’s built-in parameter features. I’ll talk more about PowerShell’s parameter features in a future article.

The ! Character

Understanding Cmd.exe’s use of the ! character requires an understanding of how it expands environment variables in batch files. One well-known behavioral quirk that causes ongoing confusion is the fact that Cmd.exe expands environment variable references before it runs a command. 

Figure 2 - Environment variable expansion problem


Figure 2 demonstrates the quirk. You might expect the letters A and B to be present in the output, but this isn’t the case. This is because Cmd.exe expands the TEST environment variable reference to an empty string before it executes the For command. 

To work around this quirk, we need to enable delayed variable expansion. To enable it, add the Enabledelayedexpansion keyword to the Setlocal command and replace the % characters surrounding the environment variable’s name with ! characters. Figure 3 shows the the updated batch file and the results. 

Figure 3 - Enable delayed variable expansion


PowerShell doesn’t suffer from this behavioral quirk, so it doesn’t need delayed variable expansion. Instead, PowerShell uses the ! character as its “not” operator (identical to -not). See the PowerShell help topic about_Logical_Operators for more information.

The ( and ) Characters

Cmd.exe uses parentheses, ( and ), to nest multi-command sequences and to support the Else statement for the If command. The rough equivalent in PowerShell is curly braces, { and }. Curly braces in PowerShell represent a chunk of code called a scriptblock. I’ll discuss how to translate the If command from batch to PowerShell in a future article.

The | Character

The vertical bar, or pipe character (|), causes the command on its right-hand side to get its input from the output from the command on its left-hand side. For example, the command 

type file.txt | sort

specifies that the Sort command should take its input from the output of the Type command. This is referred to as piping. Piped data in Cmd.exe is always text. 

PowerShell uses the | character in similar fashion, except that PowerShell inputs and outputs objects rather than text. A string of commands in PowerShell separated by | characters creates a pipeline. Pipelines are a fundamental concept in PowerShell, and I’ll discuss more about them in a later article in this series.

The < and > Characters

The < character makes Cmd.exe read input from a file, and > makes Cmd.exe redirect output to a file. Doubling the > character makes Cmd.exe append the output to an existing file instead of replacing an existing file. PowerShell supports output redirection (>, >>, etc.) using the same syntax as Cmd.exe. As of this writing, PowerShell does not yet support input redirection, but this is not a limitation in PowerShell because input redirection can be replaced by piping. For example, the command 

sort < file.txt

can be replaced by 

type file.txt | sort

The ^ Character

The ^ (caret, or hat) character has two purposes in Cmd.exe: It is both an escape character and a line continuation character. Figure 4 shows one example of each. The first ^ character in the batch file escapes the > character (i.e., the ^ prevents Cmd.exe from interpreting the > as output redirection), and the second ^ makes Cmd.exe interpret the fourth line of the batch file as the ending of the third line. 

Figure 4 - Character escaping and line continuation in Cmd.exe


PowerShell uses the backtick (`) character for escaping and line continuation rather than the ^ character. The ^ character doesn’t have a special meaning in PowerShell.

The & Character

Cmd.exe uses the & (ampersand) character as a command separator to let you put multiple commands on a single line. PowerShell uses the ; (semicolon) character instead.

The Quote (") Character

Quotation marks (") encapsulate parameters in Cmd.exe; that is, they tell Cmd.exe’s command-line parser that the text between the quotes is a single parameter rather than multiple parameters. For example, the command 

dir C:\Program Files

executes the Dir command with two parameters: C:\Program and Files. To explicitly specify to Cmd.exe that we intend a single parameter, we must quote the path; i.e.: 

dir "C:\Program Files"

The quotation marks are not themselves part of the path; they are only there to clarify your intent that the string between them is a single parameter. 

Quotes work this same way for parameters in PowerShell, although PowerShell has its own set of rules about quoting. For example, PowerShell has two kinds of quotes: Double quotes (") and single quotes ('). One of the differences between the two kinds of quotes is that PowerShell expands variable references in double-quoted strings but not single-quoted strings. The about_Quoting_Rules help topic provides the details you will need about how quoting works in PowerShell.

Clarifying Character Confusion

Cmd.exe batch files use a number of special characters that it interprets in special ways. This article gives you a first step in understanding the differences between the special characters used in Cmd.exe vs. PowerShell. 

Up next: Learn how to use PowerShell as an interactive command prompt. It can replace Cmd.exe as an interactive command shell once you learn some differences between the shells.