Nate
Nate

Reputation: 862

Out-File Of ForEach With If(Test-Path ...)

$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

Upvotes: 2

Views: 253

Answers (2)

mklement0
mklement0

Reputation: 437363

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.

Upvotes: 2

Santiago Squarzon
Santiago Squarzon

Reputation: 59900

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

Upvotes: 2

Related Questions