Avoid Blank Lines at End of a Text File with PowerShell

Avoid Blank Lines at End of a Text File with PowerShell

  • Comments 6
  • Likes

Summary: Microsoft Scripting Guy Ed Wilson teaches how to avoid writing blank lines at the end of a file by using Windows PowerShell.

 

Hey, Scripting Guy! QuestionHey, Scripting Guy! I have a problem with my script. For example, in my script, I build up a variable. Then when I write the variable to a file, I always end up with an extra blank line at the end of the text file. Though many times this is not an issue, it is, on occasion, extremely annoying. These are the times when I am creating a file that will be used to drive another script. For example, if I have a script that collects the names of computers that I need to connect to, and I then write those computer names to a text file so that I can use it with other scripts, I always end up with a blank line at the end of the file. This causes the subsequent scripts to always fail to connect to the blank line, and it therefore causes errors. So far, I have been dealing with this problem by doing a bit of error checking, but that is cumbersome, and it would be easier to avoid the problem all together. You are a wizard when it comes to this sort of stuff, so I know you will be able to solve the problem with something elegant. I remain your biggest fan,

—JN

 

Hey, Scripting Guy! AnswerHello JN,

Microsoft Scripting Guy Ed Wilson here. Wow, you are my biggest fan! Really? I mean, like the president of the Official Scripting Guys Fan Club? Cool! (Note: One advantage of being a member of the Official Scripting Guys Fan Club is that you do not have to wear round mouselike ear hats. Of course, there is nothing inherently wrong with wearing round mouselike ear hats.)

JN, your problem is that you end up with an extra blank line at the bottom of your text file or at the end of your variable. This problem is shown in the following code?

$count = "count"

for ($i = 0; $i -le 4; $i++)

{

 $count += "`r`n" + $i

}

$count > c:\fso\count.txt ; c:\fso\count.txt

When the preceding script runs, the text file contains an additional line at the end of the file. This is shown in the following figure.

Image of text file with additional blank line at end of file

One way to solve this problem is to use.NET Framework classes, instead of using redirection arrows or the Out-File cmdlet. To make the change to using the .NET Framework class, you need to make a minimal change to the previous code. The class that I need to use is found in the System.IO namespace, and it is the File class. Therefore, the complete file name is System.IO.File. This class contains a static method called WriteAllText. When writing code, it is permissible to leave off the system portion of the namespace name, because system is the root .NET Framework namespace, and it is assumed that IO would live under the System portion. When I write a script, I will generally use the complete namespace and class name; when working interactively in the Windows PowerShell console, I will generally use the shorter version of the name, and leave off the system portion of the name. The modified script that will not produce an extra blank line at the end of the file is shown here:

$count = "count"

for ($i = 0; $i -le 4; $i++)

{

 $count += "`r`n" + $i

}

 

[system.io.file]::WriteAllText("C:\fso\io.txt", $count)

 

The text file created by the preceding code is shown in the following figure.

Image of text file with no extra blank line

The System.IO.File .NET Framework class documentation is found on MSDN, but I did not need to consult it while writing the above code, not because I have everything memorized, but because I can easily use the Get-Member cmdlet to retrieve information about the WriteAllText static method. There are two ways to use the WriteAllText method. The first way is the way I used in my script: provide the path for the output file, and then provide the text. Here is the line in my script where I did just that:

[system.io.file]::WriteAllText("C:\fso\io.txt", $count)

The double colon indicates that I am calling a static method (one that is always available and does not require an instance of the class upon which to work). The first parameter is the path to the file I want to create. The c:\fso\io.txt path refers to a folder (c:\fso) on my local computer. The content from the script is stored in the $count variable, and this is the text that I write to the file.

The second way to use the WriteAllText static method from the file class in the System.IO namespace is to provide the path, the contents, and the encoding to the method call. The following figure shows the output from the Get-Member cmdlet.

Image of output from Get-Member cmdlet

To supply the encoding value to the WriteAllText static method, the third position needs an instance of the System.Text.Encoding enumeration value. The encoding enumerations are all contatined within the System.Text.Encoding class as static properties. It is easy to retrieve them by using the Get-Member cmdlet as shown here:

PS C:\Users\edwils> [system.text.encoding] | get-member -Static -MemberType property

 

   TypeName: System.Text.Encoding

 

Name                           MemberType                 Definition                                        

ASCII                             Property                        static System.Text.Encoding ASCII {get;}          

BigEndianUnicode          Property                        static System.Text.Encoding BigEndianUnicode {get;}

Default                          Property                        static System.Text.Encoding Default {get;}        

Unicode                        Property                        static System.Text.Encoding Unicode {get;}        

UTF32                           Property                        static System.Text.Encoding UTF32 {get;}          

UTF7                             Property                        static System.Text.Encoding UTF7 {get;}           

UTF8                             Property                        static System.Text.Encoding UTF8 {get;}         

 

Armed with this information, I can revise the script so that I output an ASCII-encoded file. This is shown here:

$count = "count"

for ($i = 0; $i -le 4; $i++)

{

 $count += "`r`n" + $i

}

 

[system.io.file]::WriteAllText("C:\fso\ioascii.txt", $count,[text.encoding]::ascii)

By adding the third parameter, it is the last line that is modified.

 

Well, JN, that is all about there is to writing to a text file, and ensuring that the output file does not contain any additional spaces or blank lines at the end of the file. Join me tomorrow for more exciting Windows PowerShell goodness.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

 

 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi Ed,

    this is a great solution!

    In fact, its' a really great deal for all powershell users, that we hold the .NET FW 2.0 in our hands to help us with every thing, that we can't solve ( at least not that easy) with PS 2.0 native means.

    ( I misuse Powershell sometimes as a kind of debugger for some messy, complicated code parts to test C# statements and have a look at the outcome. This is sometimes more convenient that the Visual Studio integrated debugger :-)

    As you already showed here, we have a chance to explore the .NET classes with Powershell and we can wrap composed solutions into our own functions to make them useful for us and other users ( which reminds me of ScriptingWife's profile week, ... last week :-)

    So we may make use of the best of two worlds!

    Klaus.

  • I'd like to point out that the path variable in the WriteAllText method handles relative paths strangely, so be careful. If you are in C:\Users\Name\Desktop and you define path as ".\io.txt", the file will be saved to C:\Users\Name\io.txt. Defining path as "..\io.txt" will attempt to save the file to C:\Users\io.txt. Very bizarre! I'd suggest sticking with absolute paths when using this method.

  • @JeremyEngelWork: Hi Jeremy,

    you can use relative pathnames and I'd like to swear, that the behaviour is rather predictive :-)

    Relative pathnames refer to your working directory!

    BUT: this is not necessarily the current working directory in powershell!

    It depends on how you started powershell ...

    You can find it out by [IO.Directory]::GetCurrentDirectory() e.g.

    Take a look at Jaykul's article, if you want to dig deeper into this topis:

    huddledmasses.org/powershell-power-user-tips-current-directory

    Klaus.

  • @Klaus - thanks

    @Jeremy - you are absolutely correct. This is why I emphasize using Resolve-Path and the other *path cmdlets, instead of attempting to build paths. Thanks for pointing this out.

  • [IO.File]::WriteAllText("C:\fso\ioascii.txt",((Get-Content count.txt) -join "`r`n"),[Text.Encoding]::ASCII)

  • What if I already have a file generated with the superfluous line at the end? In your example, you generate your text at the same time.