Bookmark and Share

Hey, Scripting Guy! QuestionHey, Scripting Guy! We have a number of files that we need to access at work from time to time. To protect these files, we make them read-only. This works great from a file protection standpoint, but it is really annoying when I need to make updates to the file because about half the time I will open a file, make the edits, and forget to clear the read-only attribute. When I go to save my changes, that is when I am reminded that the file is read-only and it forces me to save the changes to a new file. Then I have to go back to the folder, rename the original file, and then rename the modified file to the original file name. All this ends up costing me an extra five minutes of frustration and annoyance. I wish I had a script I could use that would clear the read-only attribute from all files in a folder. I could use that script before I start to work. Then if I had another script that would set the read-only attribute back to the files it would be great. What do you think? Could you hook me up with a couple of scripts or what?

-- LM

Hey, Scripting Guy! Answer

Hello LM, Microsoft Scripting Guy Ed Wilson here.

It is a cloudy overcast day in Charlotte, North Carolina, in the United States today. You would, of course, already know this, if you had friended me on Facebook. I am sipping a cup of Gunpowder Green Tea with a real cinnamon stick and lemon grass in it. I am listening to the Beach Boys on my newly upgraded Zune, and watching the ScriptingGuys on Twitter via my bDule software. I am in a good mood, and things are going great.

LM, I decided to write a single script that will display all read-only files in a folder, allow you to clear the read-only attribute, and then reset the read-only attribute. To use the script, you specify the path to the folder, and then use the appropriate switch to get, clear, or set the read-only attribute. The WorkWithReadOnlyFiles.ps1 script is pretty cool, and is seen here.

WorkWithReadOnlyFiles.ps1

Param ([string]$path='c:\fso',
       [switch]$get,
       [switch]$clear,
       [switch]$set,
       [switch]$help
)#end param
     
# *** Functions here ***

Function Get-ReadOnlyFiles([string]$path)
{
 "Getting readonly attribute on files in $path"
 Get-ChildItem -Path $path |
 Where-Object { $_.attributes -match 'readonly' }
} #end get-Readonlyfiles

Function Clear-ReadOnlyFiles([string]$path)
{
  "Clearing readonly attribute from files in $path"
 New-Variable -Name read_only -Value 1 -Option readonly
 Get-ChildItem -Path $path |
 Where-Object { $_.attributes -match 'readonly' } |
 ForEach-Object {
   $_.attributes = $_.attributes -Bxor $read_only }
}#end Clear-ReadonlyFiles

Function Set-ReadOnlyFiles([string]$path)
{
 "Setting readonly attribute on files in $path"
 New-Variable -Name read_only -Value 1 -Option readonly
 Get-ChildItem -Path $path |
 ForEach-Object {
   $_.attributes = [int]$_.attributes + $read_only
 }
}#end Set-ReadOnlyFiles

Function Get-HelpText
{
 $helpText = @"
  WorkWithReadOnlyFiles.ps1 -path c:\fso -get
  Gets a list of all readonly files in c:\fso folder
 
  WorkWithReadOnlyFiles.ps1 -path c:\fso -set
  Sets all files in the c:\fso folder to readonly
 
  WorkWithReadOnlyFiles.ps1 -path c:\fso -clear
  Clears the readonly attribute from all files in c:\fso folder
"@
 $helpText
}#end Get-HelpText

# *** Entry Point to Script ***
$clear = $true
if($help) { Get-HelpText ; exit }
if(!$path) { "A path is required." ; Get-HelpText ; exit }
if($get) { Get-ReadOnlyFiles -path $path ; exit }
if($clear) { Clear-ReadOnlyFiles -path $path ; exit }
if($set) { Set-ReadonlyFiles -path $path ; exit }

LM, the WorkWithReadOnlyFiles.ps1 script begins by creating some command-line parameters. The –path parameter is a string and is used to specify the target folder. The remaining parameters are all switched parameters. The –get parameter is used to tell the script to retrieve a list of all the read-only files in the folder. The –clear parameter removes the read-only attribute from all files in the target folder. The –help switch is used to display Help text on the Windows PowerShell console. The parameter section of the script is seen here.

Param ([string]$path,
       [switch]$get,
       [switch]$clear,
       [switch]$set,
       [switch]$help
)#end param

The Get-ReadOnlyFiles function is used to list the read-only files in the directory that is stored in the $path variable. This function will find files that have the read-only attribute set. An example of such a file is seen here:

Image of a read-only file

 

The Get-ReadOnlyFiles function is very useful, because by default the folder view for Windows Explorer does not list read-only files:

Image of Windows Explorer default view not shoing read-only files

 

The first thing the function does is display a status message on the Windows PowerShell console that indicates that the read-only files in the path stored in the $path variable are being identified. After the status message has been displayed, the Get-ChildItem cmdlet (which performs a functionality similar to the old DOS Dir command) is used to find all the files that are in the folder that is specified by the $path variable. Because the Get-ChildItem cmdlet’s –recurse parameter is not used, the Get-ReadOnlyFiles will only work with a single folder at a time. You could supply multiple folders via the -path parameter because the Get-ChildItem cmdlet's –path parameter will accept an array of strings; however, the $path variable is identified as a [string], which is a singleton, and not as a [string[]], which is an array. If you want to modify the script to accept multiple paths, you will need to change all the [string$path commands to [string[]]$path. If you did that, you would be able to call the script as seen here:

WorkWithReadOnlyFiles.ps1 -path "c:\fso","c:\fso1" –get

After the Get-ChildItem cmdlet retrieves the files, it passes them along the pipeline to the Where-Object cmdlet. The Where-Object cmdlet is used to look for an attribute that matches 'readonly'; when this is found, the filename, last write time, and other information about the file will be displayed. The complete Get-ReadOnlyFIles function is seen here:

Function Get-ReadOnlyFiles([string]$path)
{
 "Getting readonly attribute on files in $path"
 Get-ChildItem -Path $path |
 Where-Object { $_.attributes -match 'readonly' }
} #end get-Readonlyfiles

The Clear-ReadOnlyFiles function is used to remove the read-only attribute from all the files in the folder. A read-only variable is created that is named read_only. This variable is set to a value of 1. It is read-only because you do not want to be able to change the value of the variable, not because it is used to hold the value of the file attribute readonly. Once the read-only variable is created, the Get-ChildItem cmdlet is used to retrieve the files from the folder, and the Where-Object cmdlet is used to filter out the readonly files. The results of that are piped to the ForEach-Object cmdlet where an XOR is taken with the value of the attributes property. The -Bxor operator is used to perform the XOR operation.

For more information about performing an XOR with file attributes, refer to the Scripting Guide or to the Microsoft Press book, Microsoft PowerShell Step By Step.

When the Clear-ReadOnlyFiles function is run, the read-only attribute from all the files is removed. This is seen here:

Image of read-only file attribute being removed from all files

 

The complete Clear-ReadOnlyFiles function is seen here.

Function Clear-ReadOnlyFiles([string]$path)
{
  "Clearing readonly attribute from files in $path"
 New-Variable -Name read_only -Value 1 -Option readonly
 Get-ChildItem -Path $path |
 Where-Object { $_.attributes -match 'readonly' } |
 ForEach-Object {
   $_.attributes = $_.attributes -Bxor $read_only }
}#end Clear-ReadonlyFiles

The Set-ReadOnlyFiles function is used to set the read-only attribute on each file in the folder specified by the value of the $path variable. The first thing it does is display a status message on the Windows PowerShell console that states the read-only attribute will be set. Next, the Get-ChildItem cmdlet is used to retrieve all of the files from the folder. There is no need to use the Where-Object cmdlet to filter out files that are read-only because the read-only attribute will be applied to each file as it passes over the pipeline. The [int] class is used to convert the value in the attributes property into a number to allow us to add the number 1 (which represents a read-only file) to the value stored in the attributes property. This is seen here:

Function Set-ReadOnlyFiles([string]$path)
{
 "Setting readonly attribute on files in $path"
 New-Variable -Name read_only -Value 1 -Option readonly
 Get-ChildItem -Path $path |
 ForEach-Object {
   $_.attributes = [int]$_.attributes + $read_only
 }
}#end Set-ReadOnlyFiles

The Get-HelpText function uses a here-string to store the Help text for the script. The use of a here-string to create Help text was discussed in yesterday’s Hey, Scripting Guy! post. The complete Get-HelpText function is shown here:

Function Get-HelpText
{
 $helpText = @"
  WorkWithReadOnlyFiles.ps1 -path c:\fso -get
  Gets a list of all readonly files in c:\fso folder
 
  WorkWithReadOnlyFiles.ps1 -path c:\fso -set
  Sets all files in the c:\fso folder to readonly
 
  WorkWithReadOnlyFiles.ps1 -path c:\fso -clear
  Clears the readonly attribute from all files in c:\fso folder
"@
 $helpText
}#end Get-HelpText

The entry point to the script is used to parse the value of the command-line switches. If a switched parameter is used, the variable represented by the parameter will exist. If a switched parameter is not used, the variable will not exist. The if statement can be used to check for the presence of the variables. When a variable is detected, the appropriate function is called. Because the script will not work if you do not supply a path to check for the existence of read-only files, if the value of the $path variable is not detected, a string indicating that the path is required is displayed, and the Get-HelpText function is called. After that has been done, the script is exited. This is seen here:

if($help) { Get-HelpText ; exit }

if(!$path) { "A path is required." ; Get-HelpText ; exit }

if($get) { Get-ReadOnlyFiles -path $path ; exit }

if($clear) { Clear-ReadOnlyFiles -path $path ; exit }

if($set) { Set-ReadonlyFiles -path $path ; exit }

Well, LM, that is about all there is to working with read-only files. Hope you enjoyed this as much as I did.

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 them on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

 qch9z2mu5s