Learn about Windows PowerShell
Summary: Learn two simple Windows PowerShell methods to remove the last letter of a string.
Hey, Scripting Guy! I have, what should be a simple question. I need to be able to remove only the last character from a string. For example, if I have a string the scripts, I want the string to be the script instead. If I have the number 12345, I want the number to be 1234. I have tried everything, including split, trim, and other commands, but nothing seems to work. I thought that substring would work, but it requires me to know the length of the string, and that is something I will not always know. Can you help me?
Microsoft Scripting Guy Ed Wilson here. In the VBScript days, I used to hate string manipulation. It was not that the power of VBScript’s string manipulation language was so weak; it was simply that I did not like to monkey around with strings. Therefore, one day, I spent an entire day doing string manipulation. After that, I was a lot better, and I did not hate it so much. The reason I did not like string manipulation was that I was not that good at it. In Windows PowerShell, string manipulation is still string manipulation. The tools and the commands are similar to what I had in the VBScript days, and indeed to what is available in other languages.
Nearly everything has a concept of split, trim, substring, and other similar-sounding commands. The commands may not be called exactly the same thing, but they will offer similar functionality. In Windows PowerShell, we are actually using the methods from the string class that is found in the System .NET Framework namespace. This is good news, because the string class is very robust and complete. If you long for the actual commands used back in the VBScript days, those are available via the Microsoft.VisualBasic namespace.
DM, you mentioned the need to know the length of a string. As a matter of a fact, all strings have a length property associated with them. To find the properties of a string, I can use the Get-Member cmdlet:
“a string” | get-member –membertype property
The command and associated output are shown in the following figure.
Because I have found a length property on the string class, I can use it to obtain the length of a string. If I store a string in a variable, I can access the length property directly, as shown here:
PS C:\> $string = "the scripts"
PS C:\> $string.Length
One thing to keep in mind when working with the length property is that it reports the actual length of the string. That is, it is not zero based. The following code illustrates this:
PS C:\> "123456789".length
Though it is common to store a string in a variable and use dotted notation to retrieve the length property, the length property is also directly accessible from a string. Tab expansion does not pick it up, but it is available nonetheless. This technique is shown here:
PS C:\> "The scripts".length
The preceding commands and associated output are shown in the following figure.
DM, there are two ways to use the substring method from the string class. The first way to use the substring method defines the start location, and the method returns the remainder of the string. The second way is to specify the start location and to tell the substring method how many letters to return. I found this information by using the Get-Member cmdlet, and choosing the definition of the substring method. The command and returned information are shown here:
PS C:\> $string | gm substring | select definition
string Substring(int startIndex), string Substring(int startIndex, int length)
First, I want to show what happens when I specify a value for only the startindex parameter of the substring method. In the first example, I tell substring to begin at position 1 (the letter t in the string the scripts) and return the remainder of the string. The command and output are shown here:
PS C:\> $string.Substring(1)
In the second command, I tell the substring method to begin at position 10 and to return the remainder of the string. Because the string is 11 characters long, the command only returns the letter s. The command and associated output are shown here:
PS C:\> $string.Substring(10)
Because the length property always exists on a string, I can use it with the substring method. This is useful with the second position of the command, because I do not always know how long the string is. To begin with the first position of the string, I need to specify location 0 (remember when I used location 1, the first character returned was the letter h). If I simply use the length property, I will return the entire string. This is shown here:
PS C:\> $string.Substring(0,$string.Length)
DM, you need to return all of a string but the last letter. To do this, I simply subtract 1 from the length of the string, as shown here:
PS C:\> $string.Substring(0,$string.Length-1)
This is simply displaying the string minus the last letter of the string. To actually remove the last letter from the string, it is necessary to write the results back to the $string variable. This technique is shown here:
PS C:\> $string = $string.Substring(0,$string.Length-1)
PS C:\> $string
Another way to accomplish this, which might actually be a bit easier to do, is to use the replace operator and not supply a replacement value. The replace operator will accept regular expressions. A simple regular expression of “.$” will match the last character in a string. The dollar sign means match from the end of the string, and a period (or dot) means any single character. Therefore, the following command will replace the last character with nothing, and effectively remove the last letter of the string:
PS C:\> $string -replace ".$"
Once again, if I want to actually remove one character from the end of a string, I need to write the returned string back to the $string variable, as shown here:
PS C:\> $string = $string -replace ".$"
One of the cool things about Windows PowerShell is there are multiple ways of doing the same thing. I have illustrated two methods of removing the last character of a string. For comparison’s sake, both techniques and associated output are shown in the following figure.
DM, that is all there is to removing the last letter of a string. I invite you to 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 firstname.lastname@example.org, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy
<p>these are the best solutions to the problem!</p>
<p>I recently had a similar problem and I'm still wondering why we can't have an </p>
<p>extension to the range operator like other scripting languages have and even</p>
<p>CMD.EXE has this one:</p>
<p>C:\>set string=the scripts</p>
<p>The idea is to allow for ranges that may anchor to the end of the string.</p>
<p>Instead of </p>
<p>I would like to have</p>
<p>or if we think of arrays slices, we already have:</p>
<p>but might want to have something like:</p>
<p>Maybe the powershell team will surprise us with that sometime ???</p>
<p>Too cool the Regex!</p>
<p>@Klaus Thanks. I am glad you enjoyed the article. You can always request extensions to Windows PowerShell via the connect site. The PowerShell team pays attention to these. </p>
<p>I really like your blog and want to thank you for spending the time and effort on doing it.</p>
<p>It's genuinely excellent.</p>
<p>@Richard Awesome! Comments like this are one reason I keep at it. I am so glad that you find the content useful, and that you enjoy the Hey Scripting Guy! blog. Thank you so much for the kind words.</p>
<p>This is also a simple way that is a bit easier to read.</p>
<p>$string = $string.remove($string.length - 1)</p>
<p>String.Remove uses a zero-based index thus the "-1".</p>
<p>I have a csv file that I'm importing to extract information from each valid record. My problem is the last record has a special ascii character in it telling me it's the last record in the file. This record gets added during an ftp process that I can't control.</p>
<p>Is there a way, during the import-csv cmdlt to check the length of each record and if it is zero or say < 10 in size to bypass it?</p>
<p>@ El Bee Newbie </p>
<p>Funny you should ask. This is such an old post.</p>
<p>The short answer is - Yes!</p>
<p>Start here: <a rel="nofollow" target="_new" href="http://social.technet.microsoft.com/Forums/en-US/ITCG/threads">social.technet.microsoft.com/.../threads</a></p>
<p>THANKS so much for posting this! I was able to resolve my issue of trimming off the unwanted millisecond values from TimeSpan in a flash. (in a millisecond?) :-)</p>