Summary: Microsoft Scripting Guy, Ed Wilson, talks about using standard folders in his review of the 2014 Winter Windows PowerShell Scripting Games.
Microsoft Scripting Guy, Ed Wilson, is here. It is beginning to look like spring here in Charlotte, North Carolina. This is a welcome relief, because just a couple of weeks ago, it was like snow city. Schools closed. Businesses closed. The governor of South Carolina declared the state a disaster area and mobilized the National Guard, so it has been rather exciting around here recently. But nothing compares with the excitement of my new Surface 2 Pro that the Scripting Wife bought me. I mean, wow!
Note FTC Publishes Final Guides Governing Endorsements, Testimonials. The Microsoft Scripting Guys work for the Microsoft Corporation. The Microsoft Corporation provides me with a salary, health insurance, bonus, U.S. national holidays off, and annual leave. The Microsoft Corporation, however, has not provided me with a Surface 2 Pro. The Scripting Wife works for herself, and it is she who bought the new Surface 2 Pro with her own money. She was not coerced, intimidated, or otherwise influenced. She received no monetary considerations from any outside party. She bought it simply because she wanted to do something nice, and because she knows that the Surface 2 Pro absolutely rocks!
Nearly as exciting as having a new Surface 2 Pro to play with has been grading the 2014 Winter Scripting Games. As for winter, see the earlier paragraph —it fits. The team concept for the Games this year resulted in some excellent submissions. All of the scripts have been worthy and worthwhile submissions. Please keep my critiques in the spirit in which they are intended—mainly, they are simply some ideas that may be worthwhile to investigate.
Note This is the third in a series of blog posts in which I talk about things I noticed whilst grading submissions for the
2014 Winter Scripting Games. In case you missed the previous episodes:
- 2014 Winter PowerShell Scripting Games Wrap Up #1
Talked about best practices for using aliases and for formatting of Windows PowerShell scripts. - 2014 Winter PowerShell Scripting Games Wrap Up #2
Talked about the need for and the use of structured error handling.
Today’s post talks about using standard Windows folders from within Windows PowerShell scripts.
The need for folders
Often a script will need to write some sort of output to a file. Where I write this output can become a major design decision. There are several considerations that need to be made:
- Am I the only person who needs to access the file?
- Do others need to access the file?
- Will the file need to be accessed across the network?
- Will I have rights to the file?
- Does the folder that will contain the file exist, or does it need to be created?
If more than one user must access the file, the file needs to be stored in a shared folder, or at least a folder in which more than one user has permission to access. If a file must be accessible across the network, the file needs to be stored in a shared folder of some sort. The folder can be a local folder that is shared with the user group that needs access to the file, or it can be stored in a folder that is located on a network share.
A concern about storing a file on a network share, is that depending on the size of the data, you do not want to write directly to the file on the network share. Instead what you want to do is to write to the file locally, and then copy it to a network share.
Also remember the security model. Usually, non-elevated users do not have access to files that are stored in a root folder. Because folders inherit permissions, if you create a folder directly off the root, by default non-elevated users will not have access to that folder either. This means that you need to modify access rights to a folder if you create it immediately off the root. When you create a folder manually, this is not normally an issue (because you are there, and you remember to check the access rights). But when you create a folder via a script, it can become an issue that you were not expecting to deal with.
Using special folders can be a nice solution to security, presence, and access issues. This is because the special folders are part of a user’s profile, and the folders already have the appropriate access rights in place. The trick, of course, is knowing where to find them.
Special folders have been around forever, it seems. And just as long, scripting tools have provided ways to access the special folders. There are four main ways to access these folders:
- Use the GetSpecialFolder method from the Scripting.FileSystemObject object
- Use the SpecialFolders property from the Wscript.Shell object
- Use the NameSpace method from the Shell.Application object
- Use the GetFolderPath static method from the System.Environment .NET Framework class
Finding special folders
Environment.SpecialFolder Enumeration is documented on MSDN. Unfortunately, what is not documented is how to actually use it in Windows PowerShell. This is because the enumeration does not behave like a standard enumeration. The site is useful, however, because it describes each of the folder names, and their use in Windows.
Note I have written quite a bit about working with special folders via Windows PowerShell.
- A good post to begin with, which uses the System.Environment GetFolderPath method is The Easy Way to Use PowerShell to Work with Special Folders.
- Another approach to using the System.Environment GetFolderPath method appears in The Scripting Wife Works with Special Folders.
- An example of using the Shell.Application object appears in How to Query the Contents of a Special Folder on a Windows 7 Computer.
- An example of using the SpecialFolders property from the Wscript.Shell object appears in Create a SendTo Notepad Shortcut.
Special folders in the Windows world are useful because as a scripter I can be assured that they exist and that I will have permissions to access them. A separate subject is that of using a temporary file. It is easy to create a temporary file in the temporary folder.
Of course, I also consider it a best practice to delete temporary files after my script completes. I really consider it poor programing practice to leave temporary files lying around in the temporary folder. One reason that the temporary folder fills up with files is because it is so easy to add stuff to it. For a good approach to working with temporary files in temporary directories, see How Can I Create, Display, and Then Delete a Temporary Text File?
Working with the Environment class
Perhaps, one of the best ways to work with special folders, is to interact with the System.Environment .NET Framework class. To do this, I need to learn how to use the Environment.SpecialFolders enumeration. For whatever reason, it does not work the way I would expect it to work. In fact, it looks a little strange. I need to use [Environment+SpecialFolder] to work with the enumeration. To start with, I can pipe the type to the Get-Member cmdlet and look at the Static properties. The command is shown here:
[environment+SpecialFolder] | Get-Member -Static -MemberType property
The command and associated output look like this:
A better output of the special folder enumeration is obtainable via the [enum] .NET Framework class. The command to do that is shown here:
[enum]::GetValues([environment+SpecialFolder])
The command and output are shown in the following image:
Using the enumeration values
When I know how to find the enumeration values, the next thing I need to do is find the path to the special folder. To do this, I use the [environment] .NET Framework class, and I use the static GetFolderPath method. I then plug-in one of the values that I obtained by looking at the enumeration, or that I found on MSDN. For example, to get the path to my desktop, I use the following command:
[environment]::GetFolderPath("Desktop")
I can use this in script. For example, if I want to write a list of processes to a text file, and have that file appear on my desktop, all I need to do is:
- Get the path to the special folder
- Append the name of the file to complete the path
- Use any of the normal Windows PowerShell techniques to create the file and add data to the file
Although I can use this technique interactively from the Windows PowerShell console, I normally would not do this. To be frank, it is a bit of work. If, on the other hand, I wanted to write to files on my desktop all the time, I would entertain the idea of creating a global Windows PowerShell variable in my Windows PowerShell profile, and store the path to the desktop there. It might make a useful addition.
So here is how I use the special folder. First I get the path to the folder:
[environment]::GetFolderPath("Desktop")
Now, I append a name of a file to the path. To do this, I use the Join-Path cmdlet. Note that I must use a subexpression in my Join-Path cmdlet to force the creation of the path to the desktop first:
$desktop = Join-Path -Path $([environment]::GetFolderPath("Desktop")) -child proc.txt
Now, I get my process information and write it to a text file as shown here:
Get-Process | Select name, id | Out-File $desktop -Encoding ascii
2014 Winter PowerShell Scripting Games Wrap-Up Week continues tomorrow when I will talk about using Write-Verbose and Write-Error from within a Windows PowerShell script.
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