Wednesday, November 24, 2021

Working with PowerShell strings

One of the things that I’ve both appreciated about PowerShell but also been at times confused by PowerShell is its concepts of strings and variables. This article explains what you need to know when working with PowerShell strings.

For example, when you start writing in PowerShell, you may notice that you can use single quotes or double quotes to designate a string.

Starting simple

"A very simple string."
$string1 = "This is a string variable."
$string2 = 'This is also a string variable.'
$string1, $string2
write-host $string1, $string2
write-host $string1 $string2

Save the above as SimpleStrings_demo.ps1. And as usual, examples are available at Github.

When you run the above, you will see the following output:

Image showing output of script. Sometimes, there is a new line

The first line is echoed as is because you haven’t told PowerShell to do anything special.

Then you have created two variables and assigned strings to them. Again, in the case where there’s no assignment and no cmdlet used, PowerShell simply echos out the contents of the variables, one to a line.

When you use the write-host variable, PowerShell will again write them out, but this time keep them on the same line. It doesn’t matter here if you use a comma or not to separate them.

So far, the strings with double quotes and single quotes act the same way. But there is a difference which you’ll see in the following example:

$name = "Greg"
$age = 53
"My name is $name and my age is $age."
$string1 = "DQ: My name is $name and my age is $age."
$string2 = 'SQ: My name is $name and my age is $age.'
write-host $string1
write-host $string2

Save the above as SimpleStrings_with_variables_demo.ps1 and run it.

Your output will look like:

Image showing that variable not replaced in single-quote string

You will immediately notice that in the case of the double quotes, PowerShell did replace the inline variables of $name and $age with their values. However, in the case of single quotes, it treated the variables as part of the string itself and did not replace them.

The upside is that it’s very easy to write out strings with the value of the variables embedded directly in the string. The downside is that you can’t easily print out the variable’s name, which you may want to do while debugging a script.

The following example, SimpleStrings_with_variables_demo_2.ps1 shows a few ways of solving that problem:

$name = "Greg"
$age = 53
"My name is $name and my age is $age."
$string1 = "The `$name value is $name and the `$age value is $age."
$string2 = 'The $name value is ' + $name + ' and the $age value is ' + $age
$string3 = 'The $name value is ' + $name + " and the `$age value is $age"
write-host $string1
write-host $string2
write-host $string3

Image showing other ways to add variable to string

You will notice the backtick character ` allows you to escape the $ so that PowerShell does not treat the characters that follow as a variable name. That is generally the way I do it.

You may be tempted, and it’s perfectly legal, to build a string as shown in $string2, however, I find that harder to read and more complex and see no value to it. $string3 is simply an example showing you can use both single-quoted strings and double-quoted strings when building up a larger string. However, I would definitely avoid that one.

Dealing with objects

You will often find yourself dealing with objects when working with PowerShell. Dealing with objects introduces an issue, but fortunately, like most things, PowerShell has a solution. The following examples can be found in the Github repository in the file Strings_with_object.ps1.

Start by creating an object. For the purposes of this demo, it will be a simple instantiation and assignment.

$person1= @{Name='Greg' 
Age=53}

This object has two properties: a name and an age. Again if you simply hand the object to PowerShell and execute it, PowerShell will print something out, but a bit differently from the above examples.

Pass $person1 to PowerShell and hit enter, and you should see something like:

Image showing tabular results

This method is not terrible, but also not terribly useful. However, if you try to use the write-host cmdlet as follows: write-host $person1, you don’t get what you might expect. Instead, you get the curious response of:

Image showing that printing object shows type not value

That’s not overly helpful. If you try to put the object in between double quotes (since you know with single quotes, it will simply print out what it is handed), you will get the following:

write-host "This is all about $person1"

Image showing that adding $ to object name doesn't print value inside a string

Note the difference in the two responses. In the first case, PowerShell is attempting to parse the object and determine two properties within the object. In the second case, PowerShell doesn’t do any parsing but simply determines it is a hashtable.

The above responses provide the clues you need to get what you want. But before you get there, try the following:

$person1.Name, $person1.Age

You will get:

Image showing you can print object property

This result makes sense because now you are specifying which property you want, but it’s still far from perfect.

You may try the following, which seems like it should work:

write-host "This is all about $person1.Name"

But, curiously, this fails with the following:

Image showing that object variable with property is not replaced in string

Note that this message is a bit different from the above one that mentioned this being a Hashtable. It has .Name appended to the end. It appears that PowerShell is correctly trying to expand the object, but it’s ignoring your reference to a specific property.

The solution here is to force PowerShell to evaluate the entire object before it gets to Write-Host.

You may be tempted to try the following:

# A work around
$personName = $person1.Name
write-host "This is all about $personName"

Executing this will work:

image showing the results of a work around

But, it’s wordy and, in my opinion, a bit messy. Imagine you had an object with a dozen or more properties you wanted to write out.

The solution is to take advantage of scoping and to use parentheses as follows:

write-host "This is all about $($person1.Name) who is $($person1.Age)"

This code will return:

Image showing the result of using parentheses

This result is most likely what you wanted. PowerShell starts with the object inside the parentheses (e.g. $person1.Name) and evaluates that, and then treats it as a variable denoted by the outer $. Write-host then evaluates that variable and inserts it into the string.

Note this means you can do some additional fancy things such as the following

$person2 = @{Name="Joe"
Age=78}
write-host "Together $($person1.Name) and $($person2.Name) are a total of $($person1.Age + $person2.Age) years old."

This will produce:

Image showing summation of values from object

The summation of the ages of $person1 and $person2 happens before Write-host evaluates the outer $.

Formatting strings

Imagine you are tasked with using PowerShell to print out invoices from an order management system. With your newfound knowledge, you sit down and write something like:

$cost = 144.56
$quantity = 11
Write-host "Your cost for $quantity will be: $($cost*$quantity)"

Your output looks like:

Image showing output, cost for 11

It’s not quite perfect. You’d really like to include a currency character to the string, but you can come back to that later.

The more immediate issue is that when $quantity = 10. Then you get the following:

Note the missing trailing 0.

This problem is fortunately easy to fix. PowerShell strings allow formatting using .NET standards.

One simple way of fixing the above is the code below:

$formatted = "Your cost for {0} will be: {1:f2}" -f $quantity, $($quantity*$cost)
write-host $formatted

This will print:

That’s an improvement. The f2 is an instruction to return 2 floating-point values after the period. Additionally, you may want to put a currency symbol in there and a comma.

A simple fix would simply to do the following:

$formatted = "Your cost for {0} will be: `${1:n2}" -f $quantity, $($quantity*$cost)
write-host $formatted

Image showing output with $

Note two essential parts here. The dollar sign was added with a backtick ` so PowerShell doesn’t interpret it as the start of a variable name. In addition, by changing the f to n, PowerShell will treat this as a number using a cultural separator. This code will work equally well in a machine set up in Europe, where they tend to reverse the use of the period and comma separators. However, the currency character is still an issue which can be fixed with slightly different formatting:

$formatted = "Your cost for {0} will be: {1:c2}" -f $quantity, $($quantity*$cost)
write-host $formatted

Image showing output with currency symbol

Note that the $ is no longer needed as part of the string.

PowerShell isn’t just limited to formatting currency; you can use it for other formats. For example, say you needed to generate a list of User IDs with a specific format:

1..15 | % { 'UserID_{0:d4}' -f $_ }

The above will generate 15 User IDs of the format:

Image showing 15 user ids generated

A common requirement is formatting dates. There’s a couple of ways of doing this. If you do nothing and simply accept the defaults as follows:

$exampledate = get-date
write-host $exampledate

The results depend on your regional settings. In the US, you will get:

Image showing date and time

You could format the date when you create it:

$fdate = get-date -Format "yyyy-MM-dd"
write-host $fdate

This will return:

Image showing date only

Unfortunately, the variable $fdate contains only the information shown. You can’t later reference the time if you want.

However, there are a lot of built-in formats available:

$fdate = get-date -Format "R"
write-host $fdate

This method returns:

Image showing date with special formatting

If you want to store the full value of the returned date and later use only a formatted portion of it, you can do something like the following:

write-host $exampledate.ToString('yyyy-MM-dd')
Write-Host $exampledate.ToString("hh:mm:ss")

As you can see, there are plenty of ways to format your strings, dates, and other variables.

String padding and trimming

Often you may find yourself wanting to pad out or trim a string. You may want items to appear clearly in columns on the screen, or you may write to a file with fixed-length fields.

This task can be easily handled, as seen in the examples below. Save these as String_Padding_and_Trimming.ps1.

First, you need to generate some strings. Note that while the first variable claims to have three characters, it actually has a trailing space. This might go unnoticed in some cases if the value was read in from a database field.

$3char = "123 "  # note space!
$4char = "4567"
write-host $3char
Write-Host $4char

This looks perfectly fine as is:

Image showing numbers don't line up on right

# But what if we want them to line up, and we expect someday we might even have longer strings.
$padamount = 5
$padcharacter = ' '
write-host $3char.PadLeft($padamount,$padcharacter)
Write-Host $4char.PadLeft($padamount,$padcharacter)

This now almost works, but that trailing space is obvious:

Image showing numbers don't line up because of trailing space

Fortunately, this is fairly easy to fix using the trim() method:

write-host $3char.trim().PadLeft($padamount,$padcharacter)
Write-Host $4char.PadLeft($padamount,$padcharacter)

Image showing that using trim will align numbers on right

You can also pad to the right and, of course, select other characters. Again notice the trailing space is an issue, so in the second example, you can eliminate it.

$padamount = 7
$padcharacter = '.'
write-host $3char.PadRight($padamount,$padcharacter)
Write-Host $4char.PadRight($padamount,$padcharacter)

Image showing dots with numbers to show where the trailing space is

write-host $3char.trim().PadRight($padamount,$padcharacter)
Write-Host $4char.PadRight($padamount,$padcharacter)

You can do a lot more with the Pad and Trim methods once you fully understand them.

Adding some color to your life

Finally, what is life without some color, or, in this case, showing strings with color?

Like most things, PowerShell makes this very easy. Below is a script that was possibly written by a Hobbit to keep track of something near and dear to their stomach.

Save the following script as String_Color.ps1.

$tod = get-date
if ($tod.Hour -ge 6 -and $tod.Hour -lt 9)
    {
        write-host "Time for Breakfast!" -ForegroundColor Yellow
    }
elseif ($tod.Hour -ge 9 -and $tod.Hour -lt 11)
    {
        write-host "Time for 2nd Breakfast!" -ForegroundColor Red -BackgroundColor Green
        write-host "That's only if he knows about second breakfast!"           
    }    
elseif ($tod.Hour -ge 11 -and $tod.Hour -lt 12)
    {
        write-host "Elevenses!" -BackgroundColor Red
    }    
elseif ($tod.Hour -ge 12 -and $tod.Hour -lt 14)
    {
        write-host "Luncheon" -ForegroundColor Red -BackgroundColor Yellow
    }
else
    {
        Write-host "Afternoon Tea - 2:00 PM until 5:00 PM " -ForegroundColor Black -BackgroundColor DarkRed -NoNewline
        Write-host "Dinner - 7:00 PM until 9:00 PM " -ForegroundColor Red -BackgroundColor DarkBlue -NoNewline
        Write-host "Supper - 9:00 PM until 11:00 PM " -ForegroundColor Red -BackgroundColor DarkGreen
    }

The above script will help any Hobbit keep track of what meal they should be planning for. Note that portion control in case 12 dwarves show up unexpectedly is not covered.

If it’s early in the day, the script will simply change the foreground color of the text to yellow. This color change is done merely by adding the –foreground parameter to write-host.

In the case of second breakfast, you can set both the foreground and background colors as you desire. You will also note that any color you set is only in effect for that particular write-host cmdlet. The following one will revert back to the host colors in your terminal or IDE.

You can, as demonstrated with Elevenses, set only the background color.

Finally, if you want to have multiple colors on the same line, you can take advantage of the –NoNewLine parameter for Write-Host and suppress adding a newline character to your output. This technique can be useful if you want to build a menu option such as below: (save as Select_Option_With_Color.ps1)

write-host "1) " -ForegroundColor Red -NoNewline
write-host "select Option 1" -ForegroundColor Yellow
write-host "2) " -ForegroundColor Red -NoNewline
write-host "select Option 2" -ForegroundColor Yellow
write-host "3) " -ForegroundColor Red -NoNewline
write-host "select Option 3" -ForegroundColor Yellow
$result = Read-host "Select an option above"
if ($result -ge 1 -and $result -le 3)
    { Write-host "Thank you for selecting $result" }
else
    { "You selected an invalid value $result" }

PowerShell strings

In general, string handling in PowerShell is quite simple, but minor differences such as using double quotes or single quotes can make a difference. In addition, knowing how to escape a character or force the evaluation of a variable inside a string can make life simpler as you don’t need to add additional steps before you can output your string.

Additionally, most of the string operations shown in the article can be used when outputting to a file, making the creation of a CSV or file based on length delimited columns far easier to create.

If you like this article, you might also like PowerShell editors and environments part 2.

The post Working with PowerShell strings appeared first on Simple Talk.



from Simple Talk https://ift.tt/3cKsqtH
via

No comments:

Post a Comment