PowerShell - Farewell to For

PowerShell - Farewell to For

In my last article (Goodbye, Goto), I described how batch files handle common iteration (repeating or looping) and subroutines using the Goto and Call commands, and the most common PowerShell language constructs that replace those commands. Batch files have one last iteration command command that you still need to know how to replace in PowerShell: The For command.

 

The For command was added to Command.com in DOS over 30 years go, and originally it allowed you to run a command for each file in a set (hence the name). When Microsoft released Windows NT, they extended the For command in Cmd.exe into a more general command that can iterate files, directories, text files, and the output of commands. Over time, the Cmd.exe For command has turned into a complex command that has a number of tricky syntax quirks. In this article I’ll discuss how you can can replace each feature of the For command in PowerShell.

Iterating Files and Directories

Cmd.exe. The For /F command iterates files and directories--that is, it runs some command on each file or directory (folder) in a set. The syntax of the Cmd.exe For command to iterate files is as follows:

for [/D] %%x in (set) do command [parameters]

The set is a set of one or more files (with or without wildcards), and x is a single case-sensitive letter variable that For substitutes for each matching file’s name when it runs the specified command. The optional /D parameter instructs the For command to match against directory names instead of file names. For example, the batch file command

for %%f in (*.txt) do start notepad "%%f"

opens Notepad for each .txt file in the current directory.

There are two things to note about this command. First, it’s good practice to surround the variable with double-quotes (") to prevent parsing problems with the command. Second, if you want to type the command at the Cmd.exe prompt instead of putting the command in a batch file, you need to type only a single % character for the variable, as follows:

for %f in (*.txt) do start notepad "%f"

These are some of the syntax quirks you’re forced to deal with in Cmd.exe.

 

PowerShell. PowerShell uses the ForEach-Object cmdlet to iterate a set of files. To open Notepad for all .txt files in the current directory, you would use the following command:

Get-ChildItem *.txt | ForEach-Object {

  notepad $_

}

The vertical bar (|) character creates a pipeline, and the $_ variable means “the current object from the pipeline.” (For more information about the pipeline, see my article Presenting the PowerShell Pipeline.)

Repeating a Command in Subdirectories

Cmd.exe. The For /R command allows you to repeat a command in a directory tree (i.e., starting at a specific directory and including all of its subdirectories). The syntax is as follows:

for /R ["path"] %%x in (set) do command [parameters]

For example, the batch file command

for /R "C:\Program Files" %%p in (*.exe) do echo %%p

will list all of the .exe files in the C:\Program Files directory and its subdirectories.

 

PowerShell. To produce the same output in PowerShell, you can use the the Get-ChildItem cmdlet’s -Recurse parameter:

Get-ChildItem "C:\Program Files\*.exe" -Recurse | ForEach-Object {

  $_.FullName

}

In order to produce the same output as the batch file command, this PowerShell example outputs the FullName property of each object. (For more information about how PowerShell works with objects and output, see my article PowerShell: Objects and Output.)

Iterating a Sequence of Numbers

Cmd.exe. The For /L command iterates a sequence of numbers. It uses the following syntax:

for /L %%x in (start,step,end) do command [parameters]

When you use the For command’s /L parameter, start, step, and end are integers. The For command will repeat the command however many times you specify, starting with start, ending with end, by the amount specified by step. For example, the batch file command

for /L %%i in (5,-1,1) do echo %%i

will output a list of numbers from 5 down to 1, inclusive.

 

PowerShell. The PowerShell for statement has the same capability as the For /L command. The PowerShell equivalent would the following:

for ( $n = 5; $n -ge 1; $n-- ) {

  $n

}

 

For more information on the syntax of PowerShell’s for statement, see my article Windows PowerShell Constructs.

Iterating a Text File’s Contents

The Cmd.exe For /F command iterates the content of a text file. The syntax is as follows:

for /F ["options"] %%x in (set) do command [parameters]

The set is one or more files (wildcards are allowed). The For command opens each file and assigns the iteration variable (%%x) to each line of the file. The options string, enclosed in double quotes ("), alters how the For command interprets and parses the file content or command output. Table 1 lists the keywords available in the options string.

 

Keyword

Description

eol=c

Specifies a single end-of-line character. The For command ignores the remainder of the text on the line if it encounters the specified character.

skip=n

Skips this many lines at the beginning of the file content or command output.

delims=xyz

Specifies delimiters for each line of file content or command output. The For command will split the content into strings, or tokens, based on the delimiters specified with this keyword. The default delimiters are space and tab.

tokens=n

The For command will split each line of file content or command output by the specified numeric token positions. You can specify a comma-delimited list of token positions or a range of positions separated by a dash. The * character means “remaining text on each line after parsing the last token.”

Table 1: Keywords for the For /F options string

 

Let’s take a look at how we can replace each of these options in PowerShell.

 

eol. There are a number of ways to parse strings in PowerShell. One way to ignore the remainder of a string after a certain character is to use the IndexOf and Substring methods. For example:

$name = "Test;string"

$name.Substring(0,$name.IndexOf(";"))

The first line specifies a string containing a semicolon, and the second line of code returns the portion of the string up to (but not including) the semicolon (i.e., the string “Test”). Another technique is to use the -split operator, which splits a string into array using a character or string as a delimiter. For example:

$array = "Test;string" -split ";"

$array[0]

The first command splits the string into an array, and the second command returns the first element from the array (i.e., the string “Test”).

 

skip. You can skip lines at the beginning of input in PowerShell by using the Select-Object cmdlet with the -Skip parameter. For example, the command

Get-Content Test.txt | Select-Object -Skip 3

tells PowerShell to retrieve the content of the Test.txt but to skip the first three lines of the file.

 

delims. The PowerShell -split operator, as already noted, splits a string into an array of substrings based on one or more delimiters. For example, the command

"ABC,;DEF" -split ",;"

returns an array of two strings (“ABC” and “DEF”).

 

tokens. PowerShell arrays have numeric indices, so you can easily return any item from an array simply by specifying its index in square braces. For example:

$array = @("Able","Baker","Charlie")

$element = $array[1]

In this example, the $element variable will contain the string “Baker”.

Iterating a Command’s Output

The Cmd.exe For /F command can also iterate the output of a command. The syntax is as follows:

for /F ["options"] %%x in ('command') do command [parameters]

In other words, instead of specifying a set or one or more files, you specify a command in single quotes ('). The For command will run the command in the single quotes and assign the iteration variable (%%x) to each line of the command’s output. Aside from the single quotes, this syntax of the For /F command behaves identically whether it parses the lines in a file or the output of a command. For example, the batch file command

for /f "delims=" %%n in ('netsh wlan show networks ^| findstr "SSID"') do echo %%n

will list the wireless networks visible to the system. (This command assumes, of course, that you have an enabled wireless interface on your computer.) It goes without saying that this command is difficult to understand unless you are familiar with the For /F command.

 

In PowerShell, there’s no difference between iterating the content of a file (Get-Content) vs. iterating the output of a command. Here is one way we can write a similar command in PowerShell:

netsh wlan show networks | Select-String "SSID"

Forget about For Frustration

The For command in Cmd.exe can be hard to understand, and it has numerous syntax quirks that make it difficult to use. In contrast, iterating files, directories, numbers, text files, and commands is much simpler and more straightforward in Windows PowerShell. Use the information in this article to leave the frustration of the For command behind and write your new scripts in PowerShell instead.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish