views:

104

answers:

2

I am powershell newbie. I used a sample script and made substitute from get-item to get-content in the first line. The modified script looks like below:

$file = get-content "c:\temp\test.txt"
if ($file.IsReadOnly -eq $true)
{
$file.IsReadOnly = $false
}

So in essence I am trying to action items contained in test.txt stored as UNC paths

\\testserver\testshare\doc1.doc
\\testserver2\testshare2\doc2.doc

When running script no errors are reported and no action is performed even on first entry. Please help.

+5  A: 

Short answer:

sp (gc test.txt) IsReadOnly $false

Long answer below


Well, some things are wrong with this.

$file is actually a string[], containing the lines of your file. So the IsReadOnly property applies to the string[] and not to the actual files represented by those strings, which happen to be file names.

So, if I'm understanding you correctly you are trying to read a file, containing other file names, one on each line. And clear the read-only attribute on those files.

Starting with Get-Content isn't wrong here. We definitely are going to need it:

$filenames = Get-Content test.txt

Now we have a list of file names. To access the file's attributes we either need to convert those file names into actual FileInfo objects and operate on those. Or we pass the file names to a -Path argument of Set-ItemProperty.

I will take the first approach first and then get to the other one. So we have a bunch of file names and want FileInfo objects from them. This can be done with a foreach loop (since we need to do this for every file in the list):

$files = (foreach ($name in $filenames) { Get-Item $name })

You can then loop over the file names and set the IsReadOnly property on each of them:

foreach ($file in $files) {
    $file.IsReadOnly = $false
}

This was the long and cumbersome variant. But one which probably suits people best with no prior experience to PowerShell. You can reduce the need for having multiple collections of things lying around by using the pipeline. The pipeline transports objects from one cmdlet to another and those objects still have types.

So by writing

Get-Content test.txt | Get-Item | ForEach-Object { $_.IsReadOnly = $false }

we're achieving exactly the same result. We read the contents of the file, getting a bunch of strings. Those are passed to Get-Item which happens to know what to do with pipeline input: It treats those objects as file paths; exactly what we need here. Get-Item then sends FileInfo objects further down the pipeline, at which point we are looping over them and setting the read-only property to false.

Now, that was shorter and, with a little practise, maybe even easier. But it's still far from ideal. As I said before, we can use Set-ItemProperty to set the read-only property on the files. And we can take advantage of the fact that Set-ItemProperty can take an array of strings as input for its -Path parameter.

$files = Get-Content test.txt
Set-ItemProperty -Path $files -Name IsReadOnly -Value $false

We are using a temporary variable here, since Set-ItemProperty won't accept incoming strings as values for -Path directly. But we can inline this temporary variable:

Set-ItemProperty -Path (Get-Content test.txt) -Name IsReadOnly -Value $false

The parentheses around the Get-Content call are needed to tell PowerShell that this is a single argument and should be evaluated first.

We can then take advantage of the fact that each of those parameters is used in the position where Set-ItemProperty expects it to be, so we can leave out the parameter names and stick just to the values:

Set-ItemProperty (Get-Content test.txt) IsReadOnly $false

And then we can shorten the cmdlet names to their default aliases:

sp (gc test.txt) IsReadOnly $false

We could actually write $false as 0 to save even more space, since 0 is converted to $false when used as a boolean value. But I think it suffices with shortening here.

Joey
+2  A: 

Johannes has the scoop on the theory behind the problem you are running into. I just wanted to point out that if you happen to be using the PowerShell Community Extensions you can perform this by using the Set-Writable and Set-ReadOnly commands that are pipeline aware e.g.:

Get-Content "c:\temp\test.txt" | Set-Writable

or the short, aliased form:

gc "c:\temp\test.txt" | swr

The alias for Set-ReadOnly is sro. I use these commands weekly if not daily.

Keith Hill
Ah, nice. Looks like I should look into PSCX more deeply. Curently I only have them installed at work for Out-Clipboard.
Joey