Hey, Scripting Guy! Question

Hey, Scripting Guy! We are implementing a document management system at work, and users are no longer allowed to store PDF files. I need to search a folder for the existence of PDF files. If the folder contains any PDF files, I want to delete them. If on the other hand there are no PDF files in the folder, I would like to create a file called nopdf.txt. This is because we will use Systems Center Configuration manager software inventory to inventory computers that have the nopdf.txt file.

- RU

SpacerHey, Scripting Guy! Answer

Hi RU,

That is a rather interesting idea. Back in the days when I used to work with Systems Management Server (SMS), I used to do a similar trick to allow me easily track the progress of various operations.

For information about working with files and folders in Windows PowerShell see this article. You may also wish to see this section of "What Can I Do with Windows PowerShell" that deals specifically with files and folders. The Windows PowerShell scripting hub is a good place to get started with Windows PowerShell, and it includes download links and other items of use. The Sesame Script archive has several VBScript articles dealing with files and folders. They are worth a look for VBScript examples. The community script repository has a good selection of files and folders scripts in VBscript. You also may want to check the Hey, Scripting Guy! archive for a number of VBScript examples of working with files and folders.

Interestingly enough, with Windows PowerShell we have a short syntax that can be used from the command line. If we wanted to search a folder to see if there are any PDF files in it, and then print out a message if there were none, we could use this kind of syntax:

if(!(gci C:\fso *.pdf -R)) { "no pdf"}

The problem with this type of syntax is that it is hard to read. This is the difference between working from the command line and writing a script. The above command begins by using an if statement. The if statement has the condition that is evaluated in smooth parentheses and the code that will be executed in curly brackets. In the if statement, we use the Get-ChildItem cmdlet (or “gci”) to look for PDF files in the fso directory. The -r means to recurse. The exclamation mark, !, is used as the not operator. This is similar syntax to when your friend comes up to you and says, "You are looking good today. Not." In the same way, the not operator means if we do not find any PDF files in the fso directory, we will print out "no pdf". Let's see how we would use this in a script. The script we came up with is called CheckForPdfAndCreateMarker.ps1, and it is seen here.

$path = "c:\fso"
$include = "*.pdf"
$name = "nopdf.txt"
if(!(Get-ChildItem -path $path -include $include -Recurse)) 
  { 
    "No pdf was found in $path. Creating $path\$name marker file."
    New-Item -path $path -name $name -itemtype file -force |
    out-null
  } #end if not Get-Childitem
ELSE
 {
  $response = Read-Host -prompt "PDF files were found. Do you wish to delete <y> /<n>?"
  if($response -eq "y")
    {
     "Pdf files will be deleted."
     Get-ChildItem -path $path -include $include -recurse |
      Remove-Item
    } #end if response
  ELSE
   { 
    "PDF files will not be deleted."
   } #end else reponse
 } #end else not Get-Childitem

In the script, the first thing we do is define three variables. The first variable holds the path to the folder we are going to search, and we name it $path. The second variable holds the files we will be searching for. Because this value gets supplied to the -include parameter, I called the variable $include. Lastly, we have the name of the text file we wish to create. I call it $name. This section of code is seen here:

$path = "c:\fso"
$include = "*.pdf"
$name = "nopdf.txt"

As a best practice, when working with scripts I tend to make the variable name the same as the parameter name. This makes the code easy to read and easier to understand.

Next we need to search for the PDF files. To do this, we use the Get-ChildItem cmdlet. This cmdlet is great at searching folders, and it returns either a fileinfo object or a directoryinfo object, depending on whether the item found is a file or a folder. Because we want to search the folder specified in the -path parameter and look for the filetypes (PDF) included in the $include, variable we need to use the -recurse parameter. We are only interested in the fact that the files either exist or don’t exist. This is a Boolean condition. Just like a light switch is either on or off, something is true or false, exists or does not exist. These are all Boolean conditions. You could use an if statement like this in VBScript, but most people never did. While it might look a little strange at first, it is a nice construction. Note that the condition we are negating needs its own set of parentheses. Just like in high school algebra, things inside parentheses get operated upon before things outside the parentheses. Here is the line of code:

if(!(Get-ChildItem -path $path -include $include -Recurse))

In the code block for the if statement, we first print out a message that states no PDF file was found, and we are going to create the marker file. The cool thing here is the way the expanding strings work. An expanding string is a double quotation mark. The advantage of an expanding string is that it will expand the value of a variable when it is placed in the quotation marks. Single quotation marks are literal quotation marks. What is placed inside them is what is printed out. Here are some examples of this:

PS C:\> $a = "string"
PS C:\> "$a is a string"
string is a string
PS C:\> '$a is a string'
$a is a string
PS C:\>

In our code block, after we have printed out a message, we then create the empty file in our folder. To do this we use the New-Item cmdlet. New-Item can be used to create files, folders, and other things as well. This is why it is called New-Item instead of New-File. To create a file, we use the -itemtype of file. To create a folder we use the -itemtype of directory. It is possible the file already exists in the directory. To handle this eventuality, we use the -force parameter, which will cause the command to overwrite the existing file. We do not want to clutter the screen with confirmation messages, so we pipe the returned information to the out-null cmdlet. This code is seen here:

{ 
    "No pdf was found in $path. Creating $path\$name marker file."
    New-Item -path $path -name $name -itemtype file -force |
    out-null
  } #end if not Get-Childitem

When no PDF files are found, we see this message:

The message shown when no PDF files are found

 

In the else clause, we need to handle the situation in which there are PDF files in the folder. We could just go ahead and delete the things, and then comment out the Read-Host line and the if and else clause. But this also gives me a chance to show you how to use read-host to solicit input from the user. The Read-Host cmdlet displays a prompt. If you want to capture the user response, use a variable to hold the results of the cmdlet. In this example, I use the variable $response to hold the user response. This section of the script is seen here:

ELSE
 {
  $response = Read-Host -prompt "PDF files were found. Do you wish to delete <y> /<n>?"

Do not use $input for the input to the read-host cmdlet, as $input is an automatically created variable that is used for other things in Windows PowerShell. It will cause strange things to happen if you use this variable for your own purposes.

When we run the script and PDF files are found, we are greeted with this message:

The message shown when PDF files are found

 

We need to evaluate the response. To do this we use the if statement. In Windows PowerShell, we do not use the equals sign (=) for the equality operator. Instead, we use -eq, -ieq, and -ceq for equality. The first one is case insensitive, the second is likewise case insensitive, and the last one is the case sensitive equality operator. If the value of $response is equal to "y", we print out a message that states the PDF files will be deleted, and we once again use the Get-ChildItem cmdlet to provide a collection of all the PDF files in the folder. The cool thing is we pipeline the resulting information to the Remove-Item cmdlet, which deletes the files. This is seen here:

if($response -eq "y")
    {
     "Pdf files will be deleted."
     Get-ChildItem -path $path -include $include -recurse |
      Remove-Item
    } #end if response

When the user types "y", the screen changes as seen here:

The screen changes when the user types 'y'

 

But if the value of $response was anything other than "y", we print out a message that the files will not be deleted and we exit the script. This is seen here:

ELSE
   { 
    "PDF files will not be deleted."
   } #end else reponse
 } #end else not Get-Childitem

RU, hope U R satisfied with this script. Get it? Script you again soon.

Ed Wilson and Craig Liebendorfer, Scripting Guys