How to save powershell script results to a file - file

I am using the script below to display all the subfolders' names and the count of files in those folders. I cannot figure out how to save the results to a file. I have tried everything I could and searched online for hours.
dir -recurse | ?{ $_.PSIsContainer } | %{ Write-Host $_.FullName (dir $_.FullName | Measure-Object).Count }

Do not use Write-Host. That cmdlet writes to the console only (i.e., you cannot redirect it to a file).
Use something like this instead:
Get-ChildItem -Recurse | Where-Object { $_.PSIsContainer } | ForEach-Object {
$_.FullName
}

Found another way of doing it that lets me save to a file:
ls -rec | ? {$_.mode -match 'd'} | select FullName, #{N='Count';E={(ls $_.FullName | measure).Count}} | Out-File C:\results.txt

I took your code and I upgraded it. try this:
$OUserOut = #()
Get-ChildItem -Recurse | Where-Object { $_.PSIsContainer } | ForEach-Object {
$ObjProperties = New-Object PSObject
Add-Member -InputObject $ObjProperties -MemberType NoteProperty -Name FullName -Value $_.FullName
Add-Member -InputObject $ObjProperties -MemberType NoteProperty -Name InternalObj -Value (dir $_.FullName | Measure-Object).Count
$OUserOut += $ObjProperties
}
$OUserOut | Out-GridView -Title "Objects"
$OUserOut | Export-Csv $env:USERPROFILE\Desktop\Out.csv
The two last lines have the information for the output file. The "Out-GridView" it's a table for show you the information, and the other line it's for export the result to a csv file.

Related

wildcard in array

How can I use the 1709 as a wildcard? The value 1709 is stored in an array as $MoveItem.Version, but I can't figure out how do a -like, when the value comes from an array, as I can't put in a wildcard *. I also tried to do a match.
The file name looks like this: REFW10-X86-1709_01-12-2017.wim.
The below code works fine, but I would like to automate it, so everything comes from the array. Is that possible?
Get-ChildItem -Path $OSPathTemp -Recurse | Where {
($_.Name -eq $MoveItem.File) -and
($_.Name -like "*1709*")
} | Select-Object -ExpandProperty FullName
$MoveItem.Version contains 1607,1706,1709. I would like to choose only the one with 1709. The final output should look like this:
foreach ($MoveItem in $MoveItems) {
Get-ChildItem -Path $OSPathTemp -Recurse | Where {
($_.Name -eq $MoveItem.File) -and
($_.Name -like $MoveItem.Version)
} | Select-Object -ExpandProperty FullName
}
The Array looks like this:
$MoveItem = #(
[pscustomobject]#{File="REFW10-X86-1709_01-12-2017.wim";Version=1709}
[pscustomobject]#{File="REFW10-X86-1706_01-12-2017.wim";Version=1706}
)
So you have a hash table (or similar) named $MoveItem that has a .File property that is a filename, and you have a .Versions property that's a string array?
Test name: REFW10-X86-1709_01-12-2017.wim
Get-ChildItem -Path $OSPathTemp -Recurse |
ForEach-Object {
If ($_.Name -match '-\d{4}_') { $Version = $Matches[0] }
If ($Version -in $MoveItem.Versions -and
$_.Name -eq $MoveItem.File) { $_.FullName }
}

Powershell log deleted files

The script searches all folders and subfolders and delete the oldest file when the number of files is>5. Everything works fine, but I want also log all the delete Files as a record in a log-file.
How can I log the deleted files ?
Here the Script.
$path = "C:\test\1"
$keep = 3
$strLogFileName = "c:\test\yourlogfile.log";
$dirs = Get-ChildItem -Path $path -Recurse | Where-Object {$_.PsIsContainer}
foreach ($dir in $dirs) {
$files = Get-ChildItem -Path $dir.FullName | Where-Object {-not $_.PsIsContainer -and $_.name -like "*.zip"}
if ($files.Count -gt $keep) {
$files | Sort-Object CreationTime -desc| Select-Object -First ($files.Count - $keep) | Remove-Item -Force
***{write-host “Deleting File $File” -foregroundcolor “Red”; Remove-Item $File | out-null}***
}
}
First you will need a log-message type function in your script that will log the message to a .log file. Then chekc if the file exists and if not then create a file.
Then just before you delete your file with Remove-Item command you can use Log-Message function to log message to the log file.
% { (Log-Message "Deleting File $_"); $_ }
Complete script
$path = "C:\test\1"
$keep = 3
$strLogFileName = "c:\test\yourlogfile.log";
function Log-Message
{
Param ([string]$logtext)
Add-content $strLogFileName -value $logtext
}
$dirs = Get-ChildItem -Path $path -Recurse | Where-Object {$_.PsIsContainer}
foreach ($dir in $dirs) {
$files = Get-ChildItem -Path $dir.FullName | Where-Object {-not $_.PsIsContainer -and $_.name -like "*.zip"}
if ($files.Count -gt $keep) {
$files | Sort-Object CreationTime -desc| Select-Object -First ($files.Count - $keep) |
% { $dt=get-date;(Log-Message "Deleting File $_ on $dt");$_ }| Remove-Item -Force
}
}
You've got a good start here:
write-host “Deleting File $File” -foregroundcolor “Red”
Unfortunately Remove-Item doesn't have any output that you can mooch from, but you've already made your own output message so we can just build from that. You can pipe any output to a file by using Out-File. The append flag will attach the new content to the end of the file, and you do not have to check if the file exists.
Write-Output “Deleting File $File” | Out-File -Append logfile.txt
You don't even have to include Write-Output, if you want a shorter line.
Here is an example that shows where you need to add code. I've marked existing code with "...", and I've moved the deletion message into a variable so that you can reuse it at another location. This assumes that you've stored the selected filename in a variable.
...
if ($files.Count -gt $keep)
{
...
$message = "Deleting File $File at "+(Get-Date)
$message | Out-File -Append logfile.txt
}
...

Powershell script with multiple loops stopping after one

I am working on a script to set file permissions using the ShareUtils module and for some reason it is not continuing to my second and third stage loops. Not sure why?
$input | Where-Object {$_.AccessMask -like 2032127} | Foreach-Object {
Get-Share -Name $_.Name | Add-SharePermission $_.User Allow FullControl | Set-Share
}
$input | Where-Object {$_.AccessMask -like 1245631} | Foreach-Object {
Get-Share -Name $_.Name | Add-SharePermission $_.User Allow Change | Set-Share
}
$input | Where-Object {$_.AccessMask -like 1179817} | Foreach-Object {
Get-Share -Name $_.Name | Add-SharePermission $_.User Allow Read | Set-Share
}
Try to reset $input. Once $input is called it processes all of its elements and moves forward until it gets to the last item.
$input.reset()
UPDATE
You could also rewrite the solution, an example would be:
$input | Foreach-Object {
$perm = switch($_.AccessMask)
{
2032127 {'FullControl'; break}
1245631 {'Change'; break}
1179817 {'Read'; break}
}
if($perm)
{
Get-Share -Name $_.Name | Add-SharePermission $_.User Allow $perm | Set-Share
}
}

Export-CSV powershell loop

What is the correct practice on outputting to a CSV file from a loop. My goal is to check the attributes of all files matching a series of extensions that have not been accessed in over a month and output to a csv file for reading later.
it only outputs the information for the first file. and errors the following:
Add-Member : Cannot add a member with the name "FileName" because a member with that name already exists. If you want to overwrite the member a
nyway, use the Force parameter to overwrite it.
At C:\Users\claw\Desktop\checkFileAge.ps1:10 char:25
+ $out2csv | add-member <<<< -MemberType NoteProperty -Name FileName -Value $i.fullname
+ CategoryInfo : InvalidOperation: (#{FileName=C:\u...012 9:33:46 AM}:PSObject) [Add-Member], InvalidOperationException
+ FullyQualifiedErrorId : MemberAlreadyExists,Microsoft.PowerShell.Commands.AddMemberCommand
Here is my example:
$out2csv = New-Object PSObject;
foreach ($i in get-childitem c:\users -recurse -include *.doc, *.xls, *.ppt, *.mdb, *.docx, *.xlsx, *.pptx, *.mdbx, *.jpeg, *.jpg, *.mov, *.avi, *.mp3, *.mp4, *.ogg)
{if ($i.lastaccesstime -lt ($(Get-Date).AddMonths(-1)))
{
$out2csv | add-member -MemberType NoteProperty -Name FileName -Value $i.fullname
$out2csv | add-member -MemberType NoteProperty -Name LastAccess -Value $i.LastAccessTime
}
} $out2csv | Export-Csv "C:\FileAccessInformation.csv" -NoTypeInformation -Force
Try something like this. It is a bit more canonical PowerShell.
$ext = '*.doc', '*.xls',...
Get-ChildItem C:\Users -r -inc $ext |
Where {$_.LastAccessTime -lt [DateTime]::Now.AddMonths(-1)} |
Select FullName, LastAccessTime |
Export-Csv -NoTypeInformation -Force C:\FileAccessInformation.csv

How to delete empty subfolders with PowerShell?

I have a share that is a "junk drawer" for end-users. They are able to create folders and subfolders as they see fit. I need to implement a script to delete files created more than 31 days old.
I have that started with Powershell. I need to follow up the file deletion script by deleting subfolders that are now empty. Because of the nesting of subfolders, I need to avoid deleting a subfolder that is empty of files, but has a subfolder below it that contains a file.
For example:
FILE3a is 10 days old. FILE3 is 45 days old.
I want to clean up the structure removing files older than 30 days, and delete empty subfolders.
C:\Junk\subfolder1a\subfolder2a\FILE3a
C:\Junk\subfolder1a\subfolder2a\subfolder3a
C:\Junk\subfolder1a\subfolder2B\FILE3b
Desired result:
Delete: FILE3b, subfolder2B & subfolder3a.
Leave: subfolder1a, subfolder2a, and FILE3a.
I can recursively clean up the files. How do I clean up the subfolders without deleting subfolder1a? (The "Junk" folder will always remain.)
I would do this in two passes - deleting the old files first and then the empty dirs:
Get-ChildItem -recurse | Where {!$_.PSIsContainer -and `
$_.LastWriteTime -lt (get-date).AddDays(-31)} | Remove-Item -whatif
Get-ChildItem -recurse | Where {$_.PSIsContainer -and `
#(Get-ChildItem -Lit $_.Fullname -r | Where {!$_.PSIsContainer}).Length -eq 0} |
Remove-Item -recurse -whatif
This type of operation demos the power of nested pipelines in PowerShell which the second set of commands demonstrates. It uses a nested pipeline to recursively determine if any directory has zero files under it.
In the spirit of the first answer, here is the shortest way to delete the empty directories:
ls -recurse | where {!#(ls -force $_.fullname)} | rm -whatif
The -force flag is needed for the cases when the directories have hidden folders, like .svn
This will sort subdirectories before parent directories working around the empty nested directory problem.
dir -Directory -Recurse |
%{ $_.FullName} |
sort -Descending |
where { !#(ls -force $_) } |
rm -WhatIf
Adding on to the last one:
while (Get-ChildItem $StartingPoint -recurse | where {!#(Get-ChildItem -force $_.fullname)} | Test-Path) {
Get-ChildItem $StartingPoint -recurse | where {!#(Get-ChildItem -force $_.fullname)} | Remove-Item
}
This will make it complete where it will continue searching to remove any empty folders under the $StartingPoint
I needed some enterprise-friendly features. Here is my take.
I started with code from other answers, then added a JSON file with original folder list (including file count per folder). Removed the empty directories and log those.
https://gist.github.com/yzorg/e92c5eb60e97b1d6381b
param (
[switch]$Clear
)
# if you want to reload a previous file list
#$stat = ConvertFrom-Json (gc dir-cleanup-filecount-by-directory.json -join "`n")
if ($Clear) {
$stat = #()
} elseif ($stat.Count -ne 0 -and (-not "$($stat[0].DirPath)".StartsWith($PWD.ProviderPath))) {
Write-Warning "Path changed, clearing cached file list."
Read-Host -Prompt 'Press -Enter-'
$stat = #()
}
$lineCount = 0
if ($stat.Count -eq 0) {
$stat = gci -Recurse -Directory | %{ # -Exclude 'Visual Studio 2013' # test in 'Documents' folder
if (++$lineCount % 100 -eq 0) { Write-Warning "file count $lineCount" }
New-Object psobject -Property #{
DirPath=$_.FullName;
DirPathLength=$_.FullName.Length;
FileCount=($_ | gci -Force -File).Count;
DirCount=($_ | gci -Force -Directory).Count
}
}
$stat | ConvertTo-Json | Out-File dir-cleanup-filecount-by-directory.json -Verbose
}
$delelteListTxt = 'dir-cleanup-emptydirs-{0}-{1}.txt' -f ((date -f s) -replace '[-:]','' -replace 'T','_'),$env:USERNAME
$stat |
? FileCount -eq 0 |
sort -property #{Expression="DirPathLength";Descending=$true}, #{Expression="DirPath";Descending=$false} |
select -ExpandProperty DirPath | #-First 10 |
?{ #(gci $_ -Force).Count -eq 0 } | %{
Remove-Item $_ -Verbose # -WhatIf # uncomment to see the first pass of folders to be cleaned**
$_ | Out-File -Append -Encoding utf8 $delelteListTxt
sleep 0.1
}
# ** - The list you'll see from -WhatIf isn't a complete list because parent folders
# might also qualify after the first level is cleaned. The -WhatIf list will
# show correct breath, which is what I want to see before running the command.
To remove files older than 30 days:
get-childitem -recurse |
? {$_.GetType() -match "FileInfo"} |
?{ $_.LastWriteTime -lt [datetime]::now.adddays(-30) } |
rm -whatif
(Just remove the -whatif to actually perform.)
Follow up with:
get-childitem -recurse |
? {$_.GetType() -match "DirectoryInfo"} |
?{ $_.GetFiles().Count -eq 0 -and $_.GetDirectories().Count -eq 0 } |
rm -whatif
This worked for me.
$limit = (Get-Date).AddDays(-15)
$path = "C:\Some\Path"
Delete files older than the $limit:
Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force
Delete any empty directories left behind after deleting the old files:
Get-ChildItem -Path $path -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } | Remove-Item -Force -Recurse

Resources