$homefolder = (gci \\SERVER\homefolder | select fullname)
$outfile = "$env:USERPROFILE\Desktop\Homefolder_Desktop_Redirect.csv"
ForEach ($dir in $homefolder)
{If(Test-Path ($dir.FullName +"\Desktop")){write-host $dir.Fullname" contains desktop" -ForegroundColor Yellow
"{0:N2} GB" -f ((Get-ChildItem $dir.fullname -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum / 1GB)
}}
ForEach ($dir in $homefolder)
{If(Test-Path ($dir.FullName +"\Desktop")){}else{write-host $dir.Fullname" does not contain desktop" -ForegroundColor Red
"{0:N2} GB" -f ((Get-ChildItem $dir.fullname -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum / 1GB)
}}
I'm trying to get this to output to a file. If I put the pipe between the last 2 }} or after the last } (in each Foreach), I'm told it's empty. If I put IF inside another set of parentheses, like {(If I get If isn't valid.
If I try to write/append after 1GB) my outfile is just my script.
If I try making the Foreach($dir in $homefolder) a variable, the in is an unexpected token.
I'm sure this is something simple, but I haven't used PowerShell for much in the last 5 years... assistance is appreciated.
---UPDATE---
Thanks for the help, all!
This is what I have thanks to the assistance I've received.
$outfile = "$env:USERPROFILE\Desktop\Homefolder_Desktop_Redirect.txt"
Write-Output "Contains desktop:" | Set-Content $outfile -Force
(Get-ChildItem \\SERVER\homefolder).FullName | ForEach-Object {
if(Test-Path (Join-Path $_ -ChildPath Desktop)) {
Write-Host "$_ contains desktop" -ForegroundColor Yellow
"$_ [{0:N2} GB]" -f (
(Get-ChildItem $_ -Recurse |
Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue
).Sum / 1GB)
}
} | Add-Content $outfile -Force
Write-Output "Contains NO desktop:" | Add-Content $outfile -Force
(Get-ChildItem \\SERVER\homefolder).FullName | ForEach-Object {
if(Test-Path (Join-Path $_ -ChildPath Desktop)) {}
else{
Write-Host "$_ contains no desktop" -ForegroundColor Red
"$_ [{0:N2} GB]" -f (
(Get-ChildItem $_ -Recurse |
Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue
).Sum / 1GB)
}
} | Add-Content $outfile -Force
Invoke-Item $outfile
The main reason why PowerShell complains is because you're looking to pipe after a language keyword which is simply not possible. You can however, use ForEach-Object, a cmdlet designed to enumerate input objects from pipeline, and because it is a cmdlet and not a statement (foreach), you can pipe other cmdlets to it:
(Get-ChildItem \\SERVER\homefolder).FullName | ForEach-Object {
if(Test-Path (Join-Path $_ -ChildPath Desktop)) {
Write-Host "$_ contains desktop" -ForegroundColor Yellow
}
else {
Write-Host "$_ does not contain desktop" -ForegroundColor Red
}
$output = "$_ [{0:N2} GB]" -f (
(Get-ChildItem $_ -Recurse |
Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue
).Sum / 1GB)
# send output to the host
Write-Host $output
# send output to the success stream
$output
} | Set-Content path\to\export.txt
Generally, if you want to send the output from multiple statements to a single file, enclose them in & { ... } (or . { ... } to run directly in the caller's scope); a simplified example:
& {
foreach ($i in 1..5) { $i }
foreach ($i in 6..10) { $i }
} | Out-File test.txt
However, you can reformulate your code to a single pipeline, using the ForEach-Object cmdlet rather than the foreach loop statement:
$homefolder |
ForEach-Object {
$hasDesktop = Test-Path (Join-Path $_.FullName Desktop)
Write-Host ('{0} {1} desktop' -f $_.FullName, ('does not contain', 'contains')[$hasDesktop]) -ForegroundColor ('Red', 'Yellow')[$hasDesktop]
'Contains {0}desktop' -f ('NO ', '')[$hasDesktop]
'{0:N2} GB' -f ((Get-ChildItem $_.FullName -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum / 1GB)
} |
Out-File $outfile
Note:
Note the technique of letting a Boolean variable $hasDesktop select one of two values from an array, e.g., ('Red', 'Yellow')[$hasDesktop], which allows you to make do with a single Write-Host call to cover both the has-desktop and doesn't-have-desktop case.
This acts similar to the ternary conditional operator, available in PowerShell (Core) 7+ only; that is, the above is equivalent to: ($hasDesktop ? 'Yellow' : 'Red')
The output string is pieced together with the help of -f, the format operator.
As for your desire to print the calculated size to the display as well:
In Windows PowerShell, capture it in a variable first, write its value to the display with Write-Host, then output it to the success stream, as shown in Santiago Squarzon's helpful answer.
In PowerShell (Core) 7+, you can simply pipe to Tee-Object CON, as discussed in this answer.
Related
I have this script which parses all shares on a file server to gather information on share size, ACLs, and count of files and folders. The script works great on smaller file servers but on hosts with large shares it consumes all RAM and crashes the host, I can't seem to figure out how to optimize the script during the Get-ChildItem portion to not consume all RAM.
I found a few articles which mentioned to use a foreach loop and pipe out what I need. I am a Powershell beginner, I can't figure out how to get it to work like that. What can I try next?
$ScopeName = Read-Host "Enter scope name to gather data on"
$SavePath = Read-Host "Path to save results and log to"
$SaveCSVPath = "$SavePath\ShareData.csv"
$TranscriptLog = "$SavePath\Transcript.log"
Write-Host
Start-Transcript -Path $TranscriptLog
$StartTime = Get-Date
$Start = $StartTime | Select-Object -ExpandProperty DateTime
$Exclusions = {$_.Description -ne "Remote Admin" -and $_.Description -ne "Default Share" -and $_.Description -ne "Remote IPC" }
$FileShares = Get-SmbShare -ScopeName $ScopeName | Where-Object $Exclusions
$Count = $FileShares.Count
Write-Host
Write-Host "Gathering data for $Count shares" -ForegroundColor Green
Write-Host
Write-Host "Results will be saved to $SaveCSVPath" -ForegroundColor Green
Write-Host
ForEach ($FileShare in $FileShares)
{
$ShareName = $FileShare.Name
$Path = $Fileshare.Path
Write-Host "Working on: $ShareName - $Path" -ForegroundColor Yellow
$GetObjectInfo = Get-Childitem -Path $Path -Recurse -Force -ErrorAction SilentlyContinue
$ObjSize = $GetObjectInfo | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue
$ObjectSizeMB = "{0:N2}" -f ($ObjSize.Sum / 1MB)
$ObjectSizeGB = "{0:N2}" -f ($ObjSize.Sum / 1GB)
$ObjectSizeTB = "{0:N2}" -f ($ObjSize.Sum / 1TB)
$NumFiles = ($GetObjectInfo | Where-Object {-not $_.PSIsContainer}).Count
$NumFolders = ($GetObjectInfo | Where-Object {$_.PSIsContainer}).Count
$ACL = Get-Acl -Path $Path
$LastAccessTime = Get-ItemProperty $Path | Select-Object -ExpandProperty LastAccessTime
$LastWriteTime = Get-ItemProperty $Path | Select-Object -ExpandProperty LastWriteTime
$Table = [PSCustomObject]#{
'ScopeName' = $FileShare.ScopeName
'Sharename' = $ShareName
'SharePath' = $Path
'Owner' = $ACL.Owner
'Permissions' = $ACL.AccessToString
'LastAccess' = $LastAccessTime
'LastWrite' = $LastWriteTime
'Size (MB)' = $ObjectSizeMB
'Size (GB)' = $ObjectSizeGB
'Size (TB)' = $ObjectSizeTB
'Total File Count' = $NumFiles
'Total Folder Count' = $NumFolders
'Total Item Count' = $GetObjectInfo.Count
}
$Table | Export-CSV -Path $SaveCSVPath -Append -NoTypeInformation
}
$EndTime = Get-Date
$End = $EndTime | Select-Object -ExpandProperty DateTime
Write-Host
Write-Host "Script start time: $Start" -ForegroundColor Green
Write-Host "Script end time: $End" -ForegroundColor Green
Write-Host
$ElapsedTime = $(($EndTime-$StartTime))
Write-Host "Elapsed time: $($ElapsedTime.Days) Days $($ElapsedTime.Hours) Hours $($ElapsedTime.Minutes) Minutes $($ElapsedTime.Seconds) Seconds $($ElapsedTime.MilliSeconds) Milliseconds" -ForegroundColor Cyan
Write-Host
Write-Host "Results saved to $SaveCSVPath" -ForegroundColor Green
Write-Host
Write-Host "Transcript saved to $TranscriptLog" -ForegroundColor Green
Write-Host
Stop-Transcript
To correctly use the PowerShell pipeline (and preserve memory as each item is streamed separately), use the PowerShell ForEach-Object cmdlet (unlike the ForEach statement) and avoid assigning the pipeline to a variable (as you doing with $FileShares = ...) and don't use parenthesis ((...)) arround the the pipeline:
Get-SmbShare -ScopeName $ScopeName | Where-Object $Exclusions | ForEach-Object {
And replace all $FileShare variables in your loop with the current item: $_ variable (e.g. $FileShare.Name → $_.Name).
For the Get-Childitem part you might do the same thing (stream! meaning: use the mighty PowerShell pipeline rather than piling everything up in $GetObjectInfo):
$ObjSize = Get-Childitem -Path $Path -Recurse -Force -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue
As an aside; you might simplify your 3 size properties to a single smarter size property, see: How to convert value to KB, MB, or GB depending on digit placeholders?
addition
"But isn't putting everything into $ObjSize just swapping one variable for another?"
No it is not, think of the PowerShell pipeline as an assembly line. In this case, at the first station you take each single file information and pass it to the next (last) station where you just sum the length property and the current (file) object disposed.
Where in your question example, you read the information of all files in once and store it into $GetObjectInfo and than go to the whole list to just use (add) the length property of the (quiet heavy) PowerShell file objects.
But why don't you try it?:
Open a new PowerShell session and run:
$Path = '.'
$GetObjectInfo = Get-Childitem -Path $Path -Recurse -Force -ErrorAction SilentlyContinue
$ObjSize = $GetObjectInfo | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue
Get-Process -ID $PID
Now, open a new session again and use the PowerShell pipeline:
$Path = '.'
$ObjSize = Get-Childitem -Path $Path -Recurse -Force -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue
Get-Process -ID $PID
Notice the difference in memory usage (WS(M)).
You are buffering the entire collection of [FileSystemInfo] on $FileShare into a variable with...
$GetObjectInfo = Get-Childitem -Path $Path -Recurse -Force -ErrorAction SilentlyContinue
So, if there's a million directories and files on that share then that's a million [FileSystemInfo] instances stored in a million-element array, none of which can be garbage collected during that iteration of the foreach loop. You can use Group-Object to improve that a bit...
$groupsByPSIsContainer = Get-Childitem -Path $Path -Recurse -Force -ErrorAction SilentlyContinue |
Group-Object -Property 'PSIsContainer' -AsHashTable
# $groupsByPSIsContainer is a [Hashtable] with two keys:
# - $true gets the collection of directories
# - $false gets the collection of files
$ObjSize = $groupsByPSIsContainer[$false] | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue
$NumFiles = $groupsByPSIsContainer[$false].Count
$NumFolders = $groupsByPSIsContainer[$true].Count
...but that still ends up storing all of the [FileSystemInfo]s in the two branches of the [Hashtable]. Instead, I would just enumerate and count the results myself...
$ObjSize = 0L # Stores the total file size directly; use $ObjSize instead of $ObjSize.Sum
$NumFiles = 0
$NumFolders = 0
foreach ($fileSystemInfo in Get-Childitem -Path $Path -Recurse -Force -ErrorAction SilentlyContinue)
{
if ($fileSystemInfo.PSIsContainer)
{
$NumFolders++
}
else
{
$NumFiles++
$ObjSize += $fileSystemInfo.Length
}
}
That stores only the current enumeration result in $fileSystemInfo and never the entire sequence.
Note that if you weren't summing the files' sizes Group-Object would work well...
$groupsByIsContainer = Get-Childitem -Path $Path -Recurse -Force -ErrorAction SilentlyContinue |
Group-Object -Property 'PSIsContainer' -NoElement
$NumFiles = ($groupsByIsContainer | Where-Object -Property 'Name' -EQ -Value $false).Count
$NumFolders = ($groupsByIsContainer | Where-Object -Property 'Name' -EQ -Value $true ).Count
-NoElement prevents the resulting group objects from storing the grouped elements; we just care about the count of members in each grouping but not the members themselves. If we passed -AsHashTable then we'd lose the convenient Count property, hence why the two groups have to be accessed in this awkward way.
New to PowerShell.
Some experience with Linux, bash, and programming (Java, C, HTML/CSS/JS). I just started an internship.
I was given a PowerShell script in order to do basic disk clean-up. Part of it pasted below. It writes to both console and a logfile. Some of the servers that I am cleaning having hundreds of thousands of files. I want to increase the performance of the script by only writing to the logfile. It usually starts out pretty strong, but once the console output gets large enough, things start to slow down drastically.
I attempted to simply remove the -verbose tags, but then it doesn't write to either. Then my understanding was that 'SilentlyContinue' would allow print to log, but not console. But the code already has SilentlyContinue flags? I then tried adding-Verbose to some of the for-each statements and that didn't work either.
I'm just kind of running in circles now.
Any ideas or pointers?
function global:Write-Verbose
(
[string]$Message
)
{ # check $VerbosePreference variable
if ( $VerbosePreference -ne 'SilentlyContinue' )
{ Write-Host " $Message" -ForegroundColor 'Yellow' }
}
Write-Verbose
$DaysToDelete = 7
$LogDate = get-date -format "MM-d-yy-HH"
$objShell = New-Object -ComObject Shell.Application
$objFolder = $objShell.Namespace(0xA)
Start-Transcript -Path C:\Windows\Temp\$LogDate.log
#Cleans all code off of the screen.
Clear-Host
$Before = Get-WmiObject Win32_LogicalDisk | Where-Object { $_.DriveType -eq "3" } | Select-Object SystemName,
#{ Name = "Drive" ; Expression = { ( $_.DeviceID ) } },
#{ Name = "Size (GB)" ; Expression = { "{0:N1}" -f ( $_.Size / 1gb) } },
#{ Name = "FreeSpace (GB)" ; Expression = { "{0:N1}" -f ( $_.Freespace / 1gb ) } },
#{ Name = "PercentFree" ; Expression = { "{0:P1}" -f ( $_.FreeSpace / $_.Size ) } } |
Format-Table -AutoSize | Out-String
## Stops the windows update service.
Get-Service -Name wuauserv | Stop-Service -Force -Verbose -ErrorAction SilentlyContinue
## Windows Update Service has been stopped successfully!
## Deletes the contents of windows software distribution.
Get-ChildItem "C:\Windows\SoftwareDistribution\*" -Recurse -Force -Verbose -ErrorAction SilentlyContinue |
Where-Object { ($_.CreationTime -lt $(Get-Date).AddDays(-$DaysToDelete)) } |
remove-item -force -Verbose -recurse -ErrorAction SilentlyContinue
## The Contents of Windows SoftwareDistribution have been removed successfully!
## Deletes the contents of the Windows Temp folder.
Get-ChildItem "C:\Windows\Temp\*" -Recurse -Force -Verbose -ErrorAction SilentlyContinue |
Where-Object { ($_.CreationTime -lt $(Get-Date).AddDays(-$DaysToDelete)) } |
remove-item -force -Verbose -recurse -ErrorAction SilentlyContinue
## The Contents of Windows Temp have been removed successfully!
## Deletes all files and folders in user's Temp folder.
Get-ChildItem "C:\users\$env:USERNAME\AppData\Local\Temp\*" -Recurse -Force -ErrorAction SilentlyContinue |
Where-Object { ($_.CreationTime -lt $(Get-Date).AddDays(-$DaysToDelete)) } |
remove-item -force -Verbose -recurse -ErrorAction SilentlyContinue
## The contents of C:\users\$env:USERNAME\AppData\Local\Temp\ have been removed successfully!
## Remove all files and folders in user's Temporary Internet Files.
Get-ChildItem "C:\users\$env:USERNAME\AppData\Local\Microsoft\Windows\Temporary Internet Files\*" -Recurse -Force -Verbose -ErrorAction SilentlyContinue |
Where-Object { ($_.CreationTime -le $(Get-Date).AddDays(-$DaysToDelete)) } |
remove-item -force -recurse -ErrorAction SilentlyContinue
## All Temporary Internet Files have been removed successfully!
## Cleans IIS Logs if applicable.
Get-ChildItem "C:\inetpub\logs\LogFiles\*" -Recurse -Force -ErrorAction SilentlyContinue |
Where-Object { ($_.CreationTime -le $(Get-Date).AddDays(-60)) } |
Remove-Item -Force -Verbose -Recurse -ErrorAction SilentlyContinue
## All IIS Logfiles over x days old have been removed Successfully!
## deletes the contents of the recycling Bin.
$objFolder.items() | ForEach-Object { Remove-Item $_.path -ErrorAction Ignore -Force -Verbose -Recurse }
## The Recycling Bin has been emptied!
## Starts the Windows Update Service
Get-Service -Name wuauserv | Start-Service -Verbose
$After = Get-WmiObject Win32_LogicalDisk | Where-Object { $_.DriveType -eq "3" } | Select-Object SystemName,
#{ Name = "Drive" ; Expression = { ( $_.DeviceID ) } },
#{ Name = "Size (GB)" ; Expression = { "{0:N1}" -f ( $_.Size / 1gb) } },
#{ Name = "FreeSpace (GB)" ; Expression = { "{0:N1}" -f ( $_.Freespace / 1gb ) } },
#{ Name = "PercentFree" ; Expression = { "{0:P1}" -f ( $_.FreeSpace / $_.Size ) } } |
Format-Table -AutoSize | Out-String
## Sends some before and after info for ticketing purposes
Hostname ; Get-Date | Select-Object DateTime
Write-Host "Before: $Before"
Write-Host "After: $After"
Write-Verbose ( Get-ChildItem -Path C:\* -Include *.iso, *.vhd, *.vhdx -Recurse -ErrorAction SilentlyContinue |
Sort Length -Descending | Select-Object Name, Directory,
#{Name = "Size (GB)"; Expression = { "{0:N2}" -f ($_.Length / 1GB) } } | Format-Table |
Out-String )
## Completed Successfully!
Stop-Transcript
You want to look into Redirection: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_redirection?view=powershell-7.1
It's useful for logging your Catch output, wrapping it in ${} and specifying your stream output. Ex: ${ Write-Verbose ... } 4>&1 3>&1 2>&1 >> $logFile
Most of what you have though looks like you're trying to log data for reference. Like your Before:After & final Get-ChildItem statements. If you're doing it as you go, you can pipe to Out-File -Append to a log file. But since you have it in a block towards the end, you can simply wrap & redirect:
&{
Write-Host "Before: " $Before
Write-Host "After: " $After"
Get-ChildItem -Path C:\* -Include *.iso, *.vhd, *.vhdx -Recurse -ErrorAction SilentlyContinue |
Sort Length -Descending | Select-Object Name, Directory,
#{Name = "Size (GB)"; Expression = { "{0:N2}" -f ($_.Length / 1GB) } } | Format-Table |
Out-String
} *> C:\FilePath\File.txt
Notice you don't even need to wrap your Get-ChildItem in a Write-Verbose statement. Get cmdlets that spit out to console, will just write the output to the file when using redirection. The only time you should need to issue a Write statement is if you are adding a string of text / interpolating data.
On an unrelated note, I see a DRY opportunity. Your before & after statements are identical. Put a function towards the top of your file that returns the statement, that way you can just assign your $Before & $After vars to the return output.
The logging only traps on the IDs in my text file (get-content) it does not print the file name which gets copied
I've tried using the log option with robocopy however it only logs the last enter in my get-content text file
$Source = "F:\Temp\"
$Test = "F:\Output\"
$Daily_Results="F:\Output\Test\"
foreach ($ID in Get-Content F:\Files\files.txt) {
$ID
Get-ChildItem -Path $Source | foreach {
if($_ -match $ID) {
$Path=$Source+"$_\"
$Path
robocopy $path $test
Write-Host $Path
"File copied"
Write-Output $ID "File copied" | Out-File $Daily_Results\$(get-date -f yyyy-MM-dd)_CopyMove_Results.txt -append
Write-Output $_ | Out-File $Daily_Results\$(get-date -f yyyy-MM-dd)_CopyMove_Results.txt -append
}
}
}
What happens is that $_ gets you the full object, you need to explicitly say you want the Name.
Write-Output $_.Name | Out-File $Daily_Results\$(get-date -f yyyy-MM-dd)_CopyMove_Results.txt -append
As you are copying the files one-by-one, I see no real advantage in using robocopy here, but rather user PowerShell's own Copy-Item cmdlet.
Because you didn't say what the $ID from the text file could be, from the code you gave I gather that it is some string that must be part of the file name to copy.
This should work for you then
$Source = 'F:\Temp'
$Destination = 'F:\Output'
$Daily_Results = Join-Path -Path 'F:\Output\Test' -ChildPath ('{0:yyyy-MM-dd}_CopyMove_Results.txt' -f (Get-Date))
foreach ($ID in Get-Content F:\Files\files.txt) {
Get-ChildItem -Path $Source -File -Recurse | Where-Object { $_.Name -like "*$ID*" } | ForEach-Object {
$_ | Copy-Item -Destination $Destination -Force
Write-Host "File '$($_.Name)' copied"
# output to the log file
"$ID File copied: '$($_.Name)'" | Out-File -FilePath $Daily_Results -Append
}
}
This script gets user profiles on a machine and I am able to provide the output into a text file but I cannot figure out how to export this information to a CSV file.
dir C:\Users | foreach -Begin {} -Process {
$size = (dir $_.FullName -Recurse -Force -EA SilentlyContinue | Measure-Object ‘length’ -Sum -Maximum).Sum
Write-Output ("{0:n2}" -f ($size/1MB) + " MB", $_.FullName) >> "C:\scripts\UserProfiles\UserProfiles.txt"
}
You'll need to create an object with properties and then use Export-Csv
Get-ChildItem C:\Users |
ForEach-Object `
-Begin { Write-Host -Object "Scanning user directories" } `
-Process {
Write-Host "Scanning path '$($_.FullName)'"
$Size = (Get-ChildItem $_.FullName -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object ‘length’ -Sum -Maximum).Sum
[pscustomobject] #{
Name = $_.Name
Path = $_.FullName
Size = '{0:N2} MB' -f ( $Size / 1MB )
}
} |
Export-Csv -Path C:\Scripts\UserProfiles\UserProfiles.csv -NoTypeInformation
I'm trying to get this script to do a subdirectory compare based on filename and then only files that are 30 days or younger. The Syntax seems to acceptable but the HandbrakeCLI encoding doesn't launch.
Clear screen
$SourceDir = "\\netshare\testing\Source\*.MP4"
$DestinationDir = "\\netshare\testing\Completed_mp4\*.MP4"
$s1 = get-childitem -path $SourceDir -Recurse -Force | Where-Object {$_.LastWriteTime -gt (Get-Date).addDays(-30)}
$d1 = get-childitem -path $DestinationDir -Recurse
$results = #(compare-object $s1 $d1) | Where-Object {$_.Name -ne $_.Name}
$quantity = $results | measure
$Filecount = $quantity
$Process = 0;
foreach ($result in $results){
Write-Host -----------------------------------------------------------------
Write-Host Handbrake Batch Encoding
$Process++;
$results = $file.DirectoryName + "\" + $file.BaseName + ".MP4";
$progress = ($Process / $filecount) * 100
$progress = [Math]::Round($progress,2)
#Clear-Host
Write-Host "Processing - $results"
Write-Host "File $Process of $Filecount - $progress%"
Write-Host -------------------------------------------------------------------------------
$s1 = get-childitem -path $SourceDir -Recurse -Force | Where-Object {$_.LastWriteTime -gt (Get-Date).addDays(-30)}
$d1 = get-childitem -path $DestinationDir -Recurse
Start-Process "C:\Users\Downloads\HandBrakeCLI-1.0.1-win-x86_64.exe -ArgumentList -q 25 -i '$results' -o '$d1'"
}
$results = #(compare-object $s1 $d1) | Where-Object {$_.Name -ne $_.Name}
would be a good start to look. This will not return any results unless $_.Name is NaN (which is unlikely).
Once you fixed that there should be an error message that
C:\Users\Downloads\HandBrakeCLI-1.0.1-win-x86_64.exe -ArgumentList -q 25 -i '$results' -o '$d1'
cannot be run.
Note that you use quotation marks around the whole line, effectively telling Start-Process that the whole thing is the program to run. Which it isn't.
There's no need for Start-Process here anyway, though, you should be able to just use
C:\Users\Downloads\HandBrakeCLI-1.0.1-win-x86_64.exe -q 25 -i $results -o $d1
(Note also that due to your use of single quotes, you were passing $results and $d1 verbatim to the program instead of the variable contents. Furthermore I'm fairly sure you'd need commas between arguments with -ArgumentList instead of spaces, as that would be normal PowerShell parameter binding behaviour.)
There are a bunch of other mistakes here:
Clear screen can just be clear, cls, or Clear-Host. The screen does nothing.
$quantity = $results | measure should probably be $quantity = ($results | measure).Count or simply #($results).Count. Otherwise you won't get the output you want a few lines later.