Unable to collect *.bak files as a single object - powershell-2.0

I am new to PowerShell.
I wanted to write a simple program to list all *.bak files which I can then either sort by date or size as shown below.
$Drives = Get-WMIObject -class win32_logicaldisk -filter "DriveType = 3" ;
foreach ($d in $Drives){
If (($d.deviceId -ne "C:") -and ($d.VolumeName -ne "PAGEFILE")) {
$backups += Get-ChildItem -Path $d.deviceID -Recurse -filter *.bak
}
This generally works fine except when say for example D: drive has only one *.bak file.
In that case I get an error.
Method invocation failed because [System.IO.FileInfo] doesn't contain a method named 'op_Addition'.
At F:\work\PowerShell\DiskSpace\generate-disk-report-v2.ps1:39 char:13
+ $backups += <<<< Get-ChildItem -Path $d.deviceID -Recurse -filter *.bak
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
If I add an additional junk.bak to that drive, it works fine.

In my case, I found that the variable needs to be initialized as an array and Get-ChildItem needs to be returned as an array even, or especially, if it's only returning one file.
In your case:
$backups = #() - (Before calling Get-ChildItem)
and
$backups = #(Get-ChildItem -Path $d.deviceID -Recurse -filter *.bak) - (Cast as an array)

Related

Powershell: batch rename files, add counter in front (with leading 0)

I am trying (as so many others) to batch rename files in a folder, by adding a counter to the front of the filename with leading zeros.
Here is what I have:
b.txt
c.txt
...
zzz.txt
Here is what I want:
001_b.txt
002_c.txt
...
893_zzz.txt
My code so far:
$originalFiles = Get-ChildItem "C:\Users\abc\" -Filter *.txt
$i = 1
ForEach ($originalFile in $originalFiles) {
Rename-Item -Path $originalFile.FullName -NewName (($originalFile.Directory.FullName) + "\" + $i + $originalFile.Name)
$i++
}
I am missing the underscore between the the number and the filename. And I am missing the leading zeros.
Any suggestions are welcome.
Sorry for this basic question, this is my first PowerShell experience.
This should do the trick:
$MyPath = "C:\Users\abc\"
$i=1
Get-ChildItem -Path $MyPath -Filter "*.txt" | Sort-Object | ForEach-Object {
Rename-Item -Path $_.FullName -NewName (Join-Path -Path $_.Directory.FullName -ChildPath "$('{0:d3}' -f $i)_$($_.Name)")
$i++
}
Keep in mind that if you will have 1000 or more files, you will need more than 3 digits

Powershell wildcard on returns items

I'm trying to determine whether the directories under a specified root directory contain files that match a certain pattern, in my case RT*.dcm.
I'm using Powershell 2.0 and I first obtain all sub-directories beneath the specified root directory using
$dirList = Get-ChildItem $homeDir -recurse | where {$_.Attributes -eq 'Directory'} | Select-Object FullName
I then loop through these to see if they contain *.dcm files using (perhaps there's a better way?)
# Find files with a "dcm" extension.
$fileList = Get-ChildItem $dir.fullname | where {$_.extension -eq ".dcm"} | Select-Object FullName
# Look for directories that contain *.dcm files
if ($fileList.Count -gt 0) {
[Console]::WriteLine("Dicom directory: " + $dir.fullname)
$dicomDirList += $dir
}
The above two sections work ok
I then search through the found directories using
foreach($dir in $dicomDirList) {
$rtFileList = Get-ChildItem $dir | where {$_.name -like "RT*.dcm"} | Select-Object FullName
foreach($file in $rtFileList) {
[Console]::WriteLine("RT likey file: " + $file.fullname)
}
}
However this doesn't find the files I know that are there?
If I use
Get-ChildItem C:\myfolder\RT*.dcm
this works, but I can't figure out how to use the returned items from the previous Get-ChildItem call
Could someone please point me in the right direction?
It looks like you may be over-complicating things.
To accomplish what Get-ChildItem C:\myfolder\RT*.dcm does for the entirety of $homeDir (which is what I believe you're trying to do), you can use a single Get-ChildItem command:
Get-ChildItem $homeDir -Recurse | Where-Object{$_.Name -like "RT*.dcm"}
This searches the entirety of $homeDir recursively for all of the .dcm files you're looking for and returns them.

Comparing Registry Keys on Two Servers using Powershell

I would like to compare two Servers registry keys to make sure they both match.
Something simple like this was the initial plan:
$remote1 = (invoke-command -computername hostname `
{Get-ItemProperty "HKLM:SOFTWARE\VENDOR\APP\SUBFOLDER"})
$local1 = (invoke-command `
{Get-ItemProperty "HKLM:SOFTWARE\VENDOR\APP\SUBFOLDER"})
$compare1 = Compare-Object $local1 $remote1
That works great for one single specified key but I have multiple keys with sub folders. I can't provide a list of the ones I want to check (and loop round) as I want to make sure nothing new has been added. So I was drawn down this route to get all the keys under a specified branch in the Registry (to get me a list):
$local1 = Get-ChildItem HKLM:SOFTWARE\MICROSOFT\DirectShow -Recurse `
-ErrorAction SilentlyContinue
So I now have an object that will tell me all the registry SUBFOLDERS on the server and I could then loop round using $local1.PSPath to give me all the paths but I noticed something in the object that was interesting:
$local1 | select -first 1 -prop *
This returns:
Property : {dbl3, dbl4, dbl5, dbl6...}
PSPath : A path
PSParentPath : A Parent Path
PSChildName : 0
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
PSIsContainer : True
SubKeyCount : 0
View : Default
Handle : Microsoft.Win32.SafeHandles.SafeRegistryHandle
ValueCount : 8
Name : A Name
So the Object member "Property" contains what looks like an array of all the keys or is it a sub Object?
If it was a sub-object does it contain the keys values that I am looking to compare?
I could just snatch out the $local.Name member and loop round comparing using the code above and storing any differences but I just wondered if it would be more efficient to use the data that I already have if it contains the information I need?
I am hoping that someone could confirm that if I did:
$local1 = Get-ChildItem HKLM:SOFTWARE\MICROSOFT\DirectShow -Recurse `
-ErrorAction SilentlyContinue
$remote1 (invoke-command -computername remoteserver1 `
{Get-ChildItem HKLM:SOFTWARE\MICROSOFT\DirectShow -Recurse `
-ErrorAction SilentlyContinue})
When I do the compare am I actually comparing the keys and values match or just that keys exist? :
Compare-Object $local1 $remote1
To cut a long story short, I think I have all the data I need to compare that the Registry key values match (by running this):
$local1
Returns (extract):
Hive: HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\DirectShow
Name Property
---- --------
Debug
DoNotUse
DoNotUseDrivers32
Preferred {00001602-0000-0010-8000-00aa00389b71} : {E1F1A0B8-BEEE-490D-BA7C-066C40B5E2B9}
{e06d8032-db46-11cf-b4d1-00805f6cbbea} : {E1F1A0B8-BEEE-490D-BA7C-066C40B5E2B9}
{00000160-0000-0010-8000-00aa00389b71} : {2eeb4adf-4578-4d10-bca7-bb955f56320a}
{41564D57-0000-0010-8000-00AA00389B71} : {82d353df-90bd-4382-8bc2-3f6192b76e34}
{e06d8026-db46-11cf-b4d1-00805f6cbbea} : {212690FB-83E5-4526-8FD7-74478B7939CD}
Am I correct and does anyone know how to access individual items from the $local1 object? Taking the example above, what is a Hive? Say I wanted the "00001602-0000-0010-8000-00aa00389b71" value how would I get it from the $local1 object.
A point to note that the servers are running Powershell 2 while I am testing on Powershell 4 (I can't test on a Production server). I mention this as running $local1 on the v2 servers I get a different output in that the properties do not seem to be in pairs.
Hive: HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\DirectShow
SKC VC Name Property
--- -- ---- --------
0 0 Debug {}
0 0 DoNotUse {}
0 0 DoNotUseDrivers32 {}
0 10 Preferred {{00000050-0000-0010-8000-00AA00389B71}, {e436eb80-524f-11ce-9f53-0020af0ba77...
Is this case where the v4 objects will do what I want but the v2 won't?
Well fought my way back through the tumble-weed on this one to answer my own question. I had to assume that PowerShell V2 did not contain the key values as I couldn't find a way of extracting them so to be sure I was performing a comparison of the keys and key values I went with this:
foreach ( $KeyPath in $RegPath )
{
$_Path = (invoke-command {Get-ChildItem $KeyPath -Recurse -ErrorAction SilentlyContinue})
ForEach ($key in $_Path)
{
$local = #()
$remote = #()
ForEach ( $Property in $key.Property )
{
if ( $Key.Name -like "*HKEY_LOCAL_MACHINE*" )
{
$KeyPath = [regex]::Replace($key.Name,[regex]"HKEY_LOCAL_MACHINE\\","HKLM:")
} elseif ( $Key.Name -like "*HKEY_CURRENT_USER*" ) {
$KeyPath = [regex]::Replace($key.Name,[regex]"HKEY_CURRENT_USER\\","HKCU:")
} else {
Write-Host "I was unable to set the Registry Hive path, exiting........"
exit 1
}
$lentry = (invoke-command {Get-ItemProperty -Path $KeyPath -Name $Property})
$lfound = New-Object -TypeName PSObject
$lfound | Add-Member -Type NoteProperty -Name Path -Value $KeyPath
$lfound | Add-Member -Type NoteProperty -Name Name -Value $Property
$lfound | Add-Member -Type NoteProperty -Name Data -Value $lentry.$Property
$local += $lfound
$rentry = (invoke-command -computername remoteservername -Script {Get-ItemProperty -Path $args[0] -Name $args[1]} -Args $KeyPath, $Property -ErrorVariable errmsg 2>$null)
if ( $errmsg -like "*does not exist at path*" )
{
$Value = "KEY IS MISSING"
} else {
$Value = $rentry.$Property
}
$rfound = New-Object -TypeName PSObject
$rfound | Add-Member -Type NoteProperty -Name Path -Value $KeyPath
$rfound | Add-Member -Type NoteProperty -Name Name -Value $Property
$rfound | Add-Member -Type NoteProperty -Name Data -Value $Value
$remote += $rfound
}
$compare = Compare-Object -ReferenceObject $local -DifferenceObject $remote -Property Path,Name,Data
$results += $compare
}
}
You then have all the results in an $results object that can be worked on. I used the object in an HTML table similar to this Powershell Hash Table to HTML.
I think it would have been much simpler in PowerShell version four with no need to build $lfound and $rfound.
In powershell v4 you can just export the registry keys at whatever point you want and then use the following to compare them:
$badReg = Get-content C:\Data\badReg.reg
$goodReg = Get-ChildItem C:\Data\goodReg.reg
$results = Compare-Object -ReferenceObject $goodReg -DifferenceObject $badReg

Powershell: Count items in a folder with PowerShell

I'm trying to write a very simple PowerShell script to give me the total number of items (both files and folders) in a given folder (c:\MyFolder). Here's what I've done:
Write-Host ( Get-ChildItem c:\MyFolder ).Count;
The problem is, that if I have 1 or 0 items, the command does not work---it returns nothing.
Any ideas?
You should use Measure-Object to count things. In this case it would look like:
Write-Host ( Get-ChildItem c:\MyFolder | Measure-Object ).Count;
or if that's too long
Write-Host ( dir c:\MyFolder | measure).Count;
and in PowerShell 4.0 use the measure alias instead of mo
Write-Host (dir c:\MyFolder | measure).Count;
I finally found this link:
https://blogs.perficient.com/microsoft/2011/06/powershell-count-property-returns-nothing/
Well, it turns out that this is a quirk caused precisely because there
was only one file in the directory. Some searching revealed that in
this case, PowerShell returns a scalar object instead of an array.
This object doesn’t have a count property, so there isn’t anything to
retrieve.
The solution -- force PowerShell to return an array with the # symbol:
Write-Host #( Get-ChildItem c:\MyFolder ).Count;
If you need to speed up the process (for example counting 30k or more files) then I would go with something like this..
$filepath = "c:\MyFolder"
$filetype = "*.txt"
$file_count = [System.IO.Directory]::GetFiles("$filepath", "$filetype").Count
Only Files
Get-ChildItem D:\ -Recurse -File | Measure-Object | %{$_.Count}
Only Folders
Get-ChildItem D:\ -Recurse -Directory | Measure-Object | %{$_.Count}
Both
Get-ChildItem D:\ -Recurse | Measure-Object | %{$_.Count}
You can also use an alias
(ls).Count
Recursively count files in directories in PowerShell 2.0
ls -rec | ? {$_.mode -match 'd'} | select FullName, #{N='Count';E={(ls $_.FullName | measure).Count}}
In powershell you can to use severals commands, for looking for this commands digit: Get-Alias;
So the cammands the can to use are:
write-host (ls MydirectoryName).Count
or
write-host (dir MydirectoryName).Count
or
write-host (Get-ChildrenItem MydirectoryName).Count
To count the number of a specific filetype in a folder.
The example is to count mp3 files on F: drive.
( Get-ChildItme F: -Filter *.mp3 - Recurse | measure ).Count
Tested in 6.2.3, but should work >4.

PowerShell PSCX Read-Archive: Cannot bind parameter... problem

I'm running across a problem I can't seem to wrap my head around using the Read-Archive cmdlet available via PowerShell Community Extensions (v2.0.3782.38614).
Here is a cut down sample used to exhibit the problem I'm running into:
$mainPath = "p:\temp"
$dest = Join-Path $mainPath "ps\CenCodes.zip"
Read-Archive -Path $dest -Format zip
Running the above produces the following error:
Read-Archive : Cannot bind parameter 'Path'. Cannot convert the "p:\temp\ps\CenCodes.zip" value of type "System.String" to type "Pscx.IO.PscxPathInfo".
At line:3 char:19
+ Read-Archive -Path <<<< $dest -Format zip
+ CategoryInfo : InvalidArgument: (:) [Read-Archive], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Pscx.Commands.IO.Compression.ReadArchiveCommand
If I do not use Join-Path to build the path passed to Read-Archive it works, as in this example:
$mainPath = "p:\temp"
$path = $mainPath + "\ps\CenCodes.zip"
Read-Archive -Path $path -Format zip
Output from above:
ZIP Folder: CenCodes.zip#\
Index LastWriteTime Size Ratio Name ----- ------------- ---- ----- ----
0 6/17/2010 2:03 AM 3009106 24.53 % CenCodes.xls
Even more confusing is if I compare the two variables passed as the Path argument in the two Read-Archive samples above, they seem identical:
This...
Write-Host "dest=$dest"
Write-Host "path=$path"
Write-Host ("path -eq dest is " + ($dest -eq $path).ToString())
Outputs...
dest=p:\temp\ps\CenCodes.zip
path=p:\temp\ps\CenCodes.zip
path -eq dest is True
Anyone have any ideas as to why the first sample gripes but the second one works fine?
I created an item in the issue tracker on the CodePlex home of PSCX. Apparently this is a current known issue with PscxPathInfo. (See item #28023 in the PSCX Issue Tracker).
A work around is to do this:
Get-Item $dest | Read-Archive
Credit to r_keith_hill on CodePlex for that particular work around.

Resources