Windows PowerShell Tip of the Week

Here’s a quick tip on working with Windows PowerShell. These are published every week for as long as we can come up with new tips. If you have a tip you’d like us to share or a question about how to do something, let us know.

Reading Text Files

With the possible exception of “Dad, can I have some money?” any time someone asks the Scripting Guys a question that question invariably deals with text files. How can I read a text file? How can I read only the first five lines of a text file? How can I read only the last five lines of a text file? How can I – well, you get the idea.

Although the need for text files supposedly disappeared a long time ago, system administrators still rely on text files for much of their data storage needs. (And we’re talking good old-fashioned plain-text text files, not XML files.) In fact, when it comes to text files, about only one thing has changed in the past 10 years or so: now people want to know how to read those text files using Windows PowerShell.

People, you came to the right place.

Reading is Fundamental

By far the easiest way to read a text file from within Windows PowerShell is to use the Get-Content cmdlet. (What are the other ways? Well, for one, you could use the .NET Framework and some of the System.IO classes.) To read a text file using Get-Content just call the cmdlet followed by the path to the text file. For example, this command reads the text file C:\Scripts\Test.txt:

Get-Content C:\Scripts\Test.txt

In turn, Windows PowerShell displays the contents of the file in the console window:

Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7

Pretty slick, huh?

If you don’t want those contents displayed in the console window, at least not right away, then simply assign the returned information to a variable, like so:

$a = Get-Content C:\Scripts\Test.txt

You know, seeing as how we’ve just stored the contents of a text file in a variable, this might be a good time to explain just exactly how that information gets stored in a variable. When Get-Content reads a text file and stashes the contents in a variable, the data is stored as an array, with each line in the file (determined by the presence of a carriage return-linefeed character) representing a single item in the array. Need proof of that? Enter the command $a.GetType() and see what you get back. Here’s what you should get back:

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

Do we even care that the variable $a is an array? We should: by storing each line in the text file as a separate item in an array PowerShell makes it a snap to work with specific lines (or ranges of lines) in that file. For example, do you want to look at only the first three lines in the text file? This command will do the trick:

(Get-Content C:\Scripts\Test.txt)[0 .. 2]

What are we doing here? Well, first we’re retrieving the entire contents of the file C:\Scripts\Test.txt; note that we enclosed this command in parentheses to ensure that PowerShell completes the Get-Content task before doing anything else. After that we use PowerShell’s standard array notation to request that only array items 0 through 2 be displayed. (In PowerShell, as in many computer languages, the first item in an array is always item 0.) So what do we end up seeing on screen? This:

Line 1
Line 2
Line 3

Incidentally, another way to display just the first x number of lines in a text file is to use the –totalcount parameter, like so:

Get-Content C:\Scripts\Test.txt -totalcount 3

In the preceding command, Get-Content will retrieve only the first three lines in the file Test.txt. If you’re working with a large file this is the preferred way of displaying the first three lines, simply because those are the only lines Get-Content will retrieve. With the first command we showed you, the entire file is retrieved, but only the first three items are displayed.

Now, what about the last three lines in the text file? We’re glad you asked that question:

(Get-Content C:\Scripts\Test.txt)[-1 .. -3]

Again, we’re using standard PowerShell array notation to specify which lines of the text file we want displayed. The construction [-1 .. -3] can be broken down as follows:

  • The -1 represents the last item in the array. The index number -2 represents the second-to-the-last item in the array, and so on.

  • The two dots (..) are PowerShell’s range operator, and indicate that we want to take a range of numbers between the first value specified (-1) and the last value specified (-3).

  • The -3 (as you’ve probably figured out) represents the third-to-the-last item in the array.

The net result? This is the net result:

Line 7
Line 6
Line 5

If you want to grab specific lines from the middle of the text file then use a command like this:

(Get-Content C:\Scripts\Test.txt)[3 .. 5]

In turn, we get output that looks like this:

Line 4
Line 5
Line 6

Not bad, huh?

Here’s another commonly-asked question: how can I sort the data found in a text file? For example, suppose we have a text file that looks like this:

B
H
C
D
A
E
G
F

How can we open that file, sort the contents, and then display the sorted contents? You already know the answer to that one:

(Get-Content C:\Scripts\Test.txt) | Sort-Object

All we’re doing here is piping the contents of the text file to the Sort-Object cmdlet, then letting Sort-Object do its thing. As the name implies, its thing happens to be, well, sorting stuff:

A
B
C
D
E
F
G
H

And yes, in this case PowerShell handles the task way easier than VBScript can handle it.

Let’s try one more before we go. Suppose we’re interested in locating text files that include a specific value; for example, suppose we’re interested in tracking down all the files in a specific folder (C:\Scripts) that include the phrase Hey, Scripting Guy! How do we do that? Like this:

Select-String C:\Scripts\*.txt -pattern "Hey, Scripting Guy!"

Notice that we don’t even need to use Get-Content to open the text file; instead we simply call the Select-String cmdlet, passing Select-String two parameters:

  • The path to the file or files in question. As you can see, we can use wildcard characters when specifying the path. (The path we used retrieves all the .txt files in the folder C:\Scripts.)

  • The –pattern parameter, which represents the text we’re searching for. In this case, we’re using a simple text string (Hey, Scripting Guy!). Alternatively, we could use regular expressions when specifying a pattern.

When we run the preceding command, we get back output similar to this (assuming that the target string can be found):

C:\Scripts\Everyone.txt:3:Hey, Scripting Guy! How can I search a text file using VBScript?
C:\Scripts\Test.txt:1:Hey, Scripting Guy! How can I search a text file using Windows PowerShell?

What’s especially cool here is that each line of information is, beneath the covers, an object. Notice what happens if we retrieve this content and then pipe it to the Format-List cmdlet. First we issue this command:

Select-String C:\Scripts\*.txt -pattern "Hey, Scripting Guy!" | Format-List

And then we gaze in wonder at the following output:

IgnoreCase : True
LineNumber : 3
Line       : Hey, Scripting Guy! How can I search a text file using VBScript?
Filename   : Everyone.txt
Path       : C:\Scripts\Everyone.txt
Pattern    : Hey, Scripting Guy!

IgnoreCase : True
LineNumber : 1
Line       : Hey, Scripting Guy! How can I search a text file using Windows PowerShell?
Filename   : Test.txt
Path       : C:\Scripts\Test.txt
Pattern    : Hey, Scripting Guy!

Want to see just the file name for each file containing the target string? Try this command:

Select-String C:\Scripts\*.txt -pattern "Hey, Scripting Guy!" | Select-Object Filename

Here’s what you should get back:

Filename
--------
Everyone.txt
Test.txt

See you next week.