Learn about Windows PowerShell
Hey, Scripting Guy! In VBScript, it was easy to create a variable; all I had to do was use the DIM statement. I did not need to specify a type or a value or anything. I just used DIM and the variable would be created. How do I create variables in Windows PowerShell? Please tell me you haven’t made it harder.
Microsoft Scripting Guy Ed Wilson here. Today has been a very eventful day for me. I have had four meetings—all of which were essential. One meeting in particular, the meeting with Karen who helps us manage the Script Center Web site was especially productive. We are talking about modifying the Script Center home page to make it more user-friendly (don’t worry, it will not be a radical change like our recent migration). We all had some good ideas about what needs to be done, but I think Karen wins the best suggestion award for her idea to…well, you will just have to wait and see. I can tell you that we are really psyched!
Over the course of the last five months, we have gone from a great Web site with no bells or whistles to a great Web site with so much more available to us in terms of making the site easier for you to use. However, we won’t ever use any technical Web gewgaws just for the sake of using gewgaws. I am listening to White Snake (the early stuff, not the later commercial dross) on my Zune, and sipping a cup of Rooibos tea and nibbling on a small piece of 85 percent cacao from France while I review the e-mail sent to email@example.com.
Note: Portions of today's Hey, Scripting Guy! post are excerpted from the Microsoft Press book, Windows PowerShell 2.0 Best Practices by Ed Wilson. The book is available for pre-order.
TL, as with creating aliases (which was discussed in Monday’s Hey Scripting Guy! post), there are several different ways to create a variable and to assign a value to it. You can use the New-Item cmdlet on the variable drive as seen here:
New-Item -Name temp -Value $env:TEMP -Path variable:
You could also use the Set-Item cmdlet to do this. The advantage to the Set-Item cmdlet is that it does not generate an error if the variable already exists. Here is an example of using the Set-item cmdlet to create a variable. One thing to keep in mind is that the Set-item cmdlet does not have a parameter named –name:
Set-Item -Value $env:TEMP -Path variable:\temp
Neither the New-Item nor the Set-Item cmdlet has the ability to specify the option or the description parameter. With variables this is an important distinction because you cannot create a constant and you cannot create a read-only variable without using either Set-Variable or New-Variable. If a variable already exists and you use the Set-Variable cmdlet, the value of the variable will be overwritten if it has not been marked read-only or constant. If it is read-only, you can still modify the value of it by specifying the Force parameter. If the variable is marked as a constant, the only way to modify the value of the variable is to close the Windows PowerShell console and start over with a new value.
You can also create a variable and assign a value to it at the same time. This technique if often used when the value to be stored in the variable is the result of a calculation or concatenation. In this example, we decide to create a variable named $wuLog to store the path to the Windows Update log, which is stored in a rather obscure location in the user’s local Application Data folder. Though there is an environmental variable for local application data, the path continues to go on for a couple of levels before terminating in the WindowsUpdate.log file. As a best practice when building file paths, you should use the path cmdlets such as Join-Path to avoid concatenation errors. By using the environmental localappdata variable and Join-Path with the –resolve switched parameter, we also have a formula that will store the path to the WindowsUpdate log file on any user’s computer. This is exactly the kind of variable you would want to create and store in a user’s Windows PowerShell profile. This command is seen here.
PS C:\> $wuLog = Join-Path -Path $env:LOCALAPPDATA `-ChildPath microsoft\windows\windowsupdate.log -ResolvePS C:\> $wuLogC:\Users\edwils.NORTHAMERICA\AppData\Local\microsoft\windows\windowsupdate.log
When using a variable to hold a computed value, you are not limited to using a direct value assignment. You can use the New-Variable cmdlet to perform exactly the same task:
PS C:\> New-Variable -Name wulog -Value (Join-Path -Path $env:LOCALAPPDATA `-ChildPath microsoft\windows\windowsupdate.log -Resolve)
When using the New-Variable cmdlet to create a variable that will hold a computed result, you will often need to use parentheses to force the value to be created before attempting to assign it to the Value parameter. You may see an error about some missing or invalid parameter. This is because when the New-Variable cmdlet sees a parameter outside of a set of parentheses, it attempts to locate that parameter. An example of such an error is seen in the following image.
You can also use some of the automatic variables when creating variables for your profile. A large number of applications place files in the user’s documents directory. Though this is convenient for applications and for users who access the documents location via a link on the Start menu, it is nearly impossible to locate via the command line. An additional problem is that the Documents folder may not be displayed on the Start menu of the user. As seen in the following image, the folder may have been deselected:
To facilitate ease of access to the user’s Documents folder, you may decide to create a variable that could easily be used to refer to the path. This is another good opportunity to use the Join-Path cmdlet to aid in building the location to the Documents folder. There is an automatic variable that already points to the user's home directory. The home directory on my Windows Vista laptop points to the %username% folder under the user’s folder. This is seen here:
PS C:\> $homeC:\Users\edwils.NORTHAMERICA
Because the Documents folder is located under this home directory, as seen in the following image, we can add to this location and build the path to the documents directory:
By using the New-Variable cmdlet, you can specify the Value parameter, which is contained in a set of parentheses because of the need to resolve the value of the Join-Path command before assigning it to the docs variable. The variable will be read-only, which allows you to modify the variable if needed, but it will also be protected from accidental deletion or modification. The Description parameter provides an easy way to keep track of all the custom variables. This line of code is shown here:
New-Variable -Name docs -Value (Join-Path -Path $home -ChildPath documents) `-Option readonly -Description "MrEd Variable"
When I was first learning Windows PowerShell, I was often frustrated when I attempted to use the New-Variable, Set-Variable, and Remove-Variable cmdlets because a variable is prefixed with the dollar sign when working at the command line, but the name parameter does not use the dollar sign as part of the name of the variable.
Another way to obtain the path to the Documents folder is to use the WshShell object from VBScript fame. Because Windows PowerShell provides easy access to Component Object Model (COM) objects, there is no reason to avoid these devices. One way to use this COM object is to create and use the object all in the same line. This is seen here:
$docs = (New-Object -ComObject Wscript.Shell).specialFolders.item("MyDocuments")
From a best practice standpoint, there are at least two problems with the above syntax. The most obvious issue is that it is not very readable. While this is rather common usage, it would better to split the command into two lines of code as seen here:
$wshShell = New-Object -ComObject Wscript.Shell$docs = $wshShell.SpecialFolders.Item("MyDocuments")
Well, TL, this should get you started working with variables in Windows PowerShell. Join us tomorrow as we open the virtual mail bag and answer questions that require only shorter answers.
If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at firstname.lastname@example.org or post them on the Official Scripting Guys Forum. See you tomorrow. Until then, keep on scripting!
Ed Wilson and Craig Liebendorfer, Scripting Guys