Search code examples
powershellloopsio-redirection

How to redirect a foreach loop output to a file?


I'm trying to redirect a foreach loop output to a file with the following powershell code :

PS C:\> foreach ( $dir in "c:\ProgramData", "c:\Program Files", "c:\Program Files (x86)" ) { echo "=> $dir :" ; dir -r -fo $dir 2>$null | % FullName | sls 'exe$' } > C:\temp\VDI_Exclustion_List.txt

but the output is displayed on stdout and I get this error :

> : The term '>' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:157
+ ... ir :" ; dir -r -fo $dir 2>$null | % FullName | sls 'exe$' } > C:\temp ...
+                                                                 ~
    + CategoryInfo          : ObjectNotFound: (>:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException


PS C:\> ls C:\temp\VDI_Exclustion_List.txt
ls : Cannot find path 'C:\temp\VDI_Exclustion_List.txt' because it does not exist.
At line:1 char:1
+ ls C:\temp\VDI_Exclustion_List.txt
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\temp\VDI_Exclustion_List.txt:String) [Get-ChildItem], ItemNotFoundEx
   ception
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

PS C:\>

First, I tried replacing the > redirection by | Out-File C:\temp\VDI_Exclustion_List.txt but that did not work :

At line:1 char:138
+ ... 6)" ) { dir -r -fo $dir 2>$null | % FullName | sls 'exe$' } | Out-Fil ...
+                                                                 ~
An empty pipe element is not allowed.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : EmptyPipeElement



PS C:\>

Then I tried replacing the > redirection by 6>&1 | Out-File C:\temp\VDI_Exclustion_List.txt (found here) but that did not work either :

PS C:\> foreach ( $dir in "c:\ProgramData", "c:\Program Files", "c:\Program Files (x86)" ) { echo "=> $dir :" ; dir -r -fo $dir 2>$null | % FullName | sls 'exe$' } 6>&1 | Out-File C:\temp\VDI_Exclustion_List.txt
......
6>&1 : The term '6>&1' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try
again.
At line:1 char:157
+ ... :" ; dir -r -fo $dir 2>$null | % FullName | sls 'exe$' } 6>&1 | Out-F ...
+                                                              ~~~~
    + CategoryInfo          : ObjectNotFound: (6>&1:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException


PS C:\> 

I expect the output to be inside the file C:\temp\VDI_Exclustion_List.txt but the file is not even created.


Solution

  • As you've already found, flow control statements can't be used in place of commands or value expressions - but you can wrap them in a scriptblock and then redirect the output from invoking that instead:

    & {
      foreach ($dir in "c:\ProgramData", "c:\Program Files", "c:\Program Files (x86)") { 
        echo "=> $dir :" 
        dir -r -fo $dir 2>$null | % FullName | sls 'exe$'
      }
    } > C:\temp\VDI_Exclustion_List.txt
    

    That being said, you don't actually need a loop here!

    You can pipe your input data directly to Get-ChildItem, the files discovered by Get-ChildItem can in turn be piped to Where-Object to filter on their extension, at which point we can grab the FullName property value from each resulting subset of files with the ForEach-Object command:

    "c:\ProgramData", "c:\Program Files", "c:\Program Files (x86)" |Get-ChildItem -Recurse -File -Force |Where-Object Extension -eq '.exe' |ForEach-Object FullName |Set-Content C:\temp\VDI_Exclustion_List.txt