Hey, Scripting Guy! Ignoring errors or bubbling up errors to the user of a script seems a bit inefficient. I mean, wouldn’t it be better to avoid the errors in the first place? I am only a beginner when it comes to writing scripts, and maybe I just do not get it, but why can’t you just write code that does not generate errors?
— KE
Hello KE,
Microsoft Scripting Guy Ed Wilson here. One of the bad things about having a day filled with meetings is that it really disturbs one’s schedule, disrupts one’s rhythm, and derails the ebb and flow. Not that meetings are necessarily a bad thing, but there is a cost that must be factored into the equation—a cost not just measured in labor for meeting attendance. After all, there is meeting prep work, meeting follow-up work, and then the mental context switching that occurs when shifting from one task to a meeting and then back to the task. After spending most of yesterday in meetings, my workspace feels foreign, my Zune HD seems neglected, and my teapot is missing. To ease back into normal work mode, I thought I would catch up on some of the e-mail sent to scripter@microsoft.com.
KE, you are correct. Preventing errors is much more efficient than trapping errors. At times, this is relatively easy to do; on other occasions, it can be problematic to implement. There are a number of occasions when a few simple checks will improve the reliability of your script.
One of my favorite cmdlets is Test-Path. I use it before file and registry operations. In the Windows PowerShell shell, you can use the Test-Path cmdlet to see if a file exists. The cmdlet returns a Boolean value, and true means the file exists. It will return false if the file does not exist. This is shown here:
PS C:> test-path -Path c:fsotest.txt
True
PS C:>
If I am developing a script that will write to the test.txt file in the C:fso folder, I have a number of choices that I must make before getting too far in the process. The simplest solution is to delete the file if it exists, and create a new one. However, that may not be an acceptable solution. You may want to append to the file, or you may want to create a new file with a similar name. The “correct” solution is up to you, but it all hinges on detecting the existence of the file. This is where the Test-Path comes into play.
Because the first task is to see if the file exists or not, use an if statement. In reality, we only need to know if the file does not exist. If it does not exist, we can create it. Because the Test-Path cmdlet returns true or false if the file exists, and we want to know if the file does not exist, we use the not operator to reverse the logic. This is shown here:
if(!(Test-Path -Path $path))
If the file does not exist, create it by using the New-Item cmdlet. This is shown here:
new-item -Path $path -Value “new file” –itemtype file
If the file does exist, it will trigger the Else clause. This is where we have to decide what we want to do if the file exists. In this example, we append to the text file by using the Add-Content cmdlet. This is shown here:
Add-Content -Path $path -Value “`r`n Additional content”
CheckForFileAndAddContent.ps1
$path = “c:fsotest.txt”
if(!(Test-Path -Path $path))
{
new-item -Path $path -Value “new file” –itemtype file
}
else
{
Add-Content -Path $path -Value “`r`n Additional content”
}
Each time the script runs, the words “Additional content” are added to the bottom of the Test.txt file. This is shown in the following image.
Use the same technique when working with the registry. As seen in the CheckForRegistryKeyAndUpdate.ps1, you do not have to use the exclamation point (!) for the not operator. You can simply use –not if you wish. It makes the script more readable, but in general, I do not use it because of th e extra typing that is involved. When working with the registry, it is important to remember that the Test-Path cmdlet will not report correctly on a registry entry (registry key property); it always reports false. It does, however, correctly report on the registry keys themselves. The snippet is shown here (compare these paths with those seen in the next image in this post):
PS C:> Test-Path -Path HKCU:SoftwareScriptingGuysTestupdate
False
PS C:> Test-Path -Path HKCU:SoftwareScriptingGuysTest
True
PS C:>
Use the Test-Path cmdlet to check the path to the registry key, and if it does not exist, create both the registry key and the registry key property. This is seen here:
If(-not(Test-Path -Path $path))
{
New-Item -Path $path -Force
New-ItemProperty -Path $path -Name Update -Value “Updated”
}
If the registry key does exist, we cannot determine if the registry key property exists; therefore, we use the Set-ItemProperty cmdlet. This will create the registry key property or update the key if it exists, which for our purpose is sufficient. This command is shown here:
Set-ItemProperty -Path $path -Name Update -Value “New Update”
The completed CheckForRegistryKeyAndUpdate.ps1 script is seen here.
CheckForRegistryKeyAndUpdate.ps1
$path = “HKCU:SoftwareScriptingGuysTest”
If(-not(Test-Path -Path $path))
{
New-Item -Path $path -Force
New-ItemProperty -Path $path -Name Update -Value “Updated”
}
Else
{
Set-ItemProperty -Path $path -Name Update -Value “New Update”
}
When the script runs, the registry entries shown in the following image appear.
The cool thing about the Test-Path cmdlet is that it works with other Windows PowerShell providers. For example, before creating a new alias, you may wish to use the Test-Path cmdlet to see if the alias exists. This is shown here:
PS C:> Test-Path -Path alias:gc
True
PS C:>
You could use this in a script as seen in CheckForAliasAndCreate.ps1. You can see that the process works in a similar manner as the other scripts we have created today.
CheckForAliasAndCreate.ps1
$path = “Alias:gc”
if(!(Test-Path -Path $path))
{
New-Alias -Name gc -Value Get-Credential
}
else
{
“Alias $path already exists”
}
If you wish to check on the existence of a function, use the function drive with the Test-Path cmdlet as shown here:
PS C:> Test-Path -Path Function:TabExpansion
True
PS C:>
You can use the Test-Path cmdlet to check for variables, by inspecting the variable drive. This is shown here:
PS C:> Test-Path -Path Variable:PSBoundParameters
True
PS C:>
This also works for the environmental drive, as shown here:
PS C:> Test-Path -Path Env:ALLUSERSPROFILE
True
PS C:>
If you need to know if a user has a particular certificate in the certificate store, use the certificate drive as shown here:
PS C:> Test-Path -Path cert:CurrentUserAuthRootC31EB1E720E33C8102BADC37D44DE5D4674752A8
True
PS C:>
As you can see, using the Test-Path cmdlet with the certificate drive can pose some challenges, because you have to use the thumbprint of the certificate.
KE, that is all there is to using the Test-Path cmdlet to check for resources before taking action. Error Handling Week will continue tomorrow when we will talk about…wait a minute.
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 scripter@microsoft.com or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson and Craig Liebendorfer, Scripting Guys
0 comments