Search code examples
powershellforeachautomationscriptingfile-management

Powershell: Issue with foreach loop. "Cannot convert the value of type "System.String" to type "System.Management.Automation.SwitchParameter"


I have a report that I have automated. the report is a CSV file, and it is split every 80,000 lines, so I regularly end up with reports in multiple parts. I have a large function that processes the report perfectly fine when I have a single file, but I am having issues with ingesting multiple files at the same time. The report filenames are formatted as such:

Part_1.csv
Part_2.csv
Part_3.csv

What I'm doing is getting the last downloaded file, and checking to see how many parts it had, like this:

$FileName = Get-ChildItem $env:USERPROFILE\Downloads | where Extension -like .csv | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1
$FileCount = [int]$FileName.basename.substring($FileName.Basename.Length-1)

If the I get isn't Part_1.csv I know I need to load multiple files, and I'm doing that with this:

 if ($FileCount -ne "1")
        {
        $List = Get-ChildItem $env:USERPROFILE\Downloads | where Extension -like .csv | Sort-Object -Property LastWriteTime -Descending | Select-Object -First $FileCount | Sort-object       

After this step, $List contains the following:

PS C:\> $List

    Directory: C:\Users\username\Downloads

Mode                LastWriteTime         Length Name                                                                                                                                  
----                -------------         ------ ----                                                                                                                                  
-a----        2/18/2025   3:25 PM       16930338 Report_Part_1.csv
-a----        2/18/2025   3:25 PM       16930338 Report_Part_2.csv                                                                                                
-a----        2/18/2025   3:26 PM        1448835 Report_Part_3.csv                                                                                                  

PS C:\> 

I then load each different part of the report with a foreach loop:

Foreach($File in $List.FullName)
    {
    $Object += Import-Csv -Path $File
    }

Here's this entire part of the script in one block for you.

$Object = @()

$FileName = Get-ChildItem $env:USERPROFILE\Downloads | where Extension -like .csv | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1

$FileCount = [int]$FileName.basename.substring($FileName.Basename.Length-1)

if ($FileCount -ne "1")
    {
    $List = Get-ChildItem $env:USERPROFILE\Downloads | where Extension -like .csv | Sort-Object -Property LastWriteTime -Descending | Select-Object -First $FileCount | Sort-object
    Foreach($File in $List.FullName)
        {
        $Object += Import-Csv -Path $File
        }
    }
Else
    {
    $Object = Import-Csv -Path $FileName.FullName
    }

Here's the kicker, if I run this isolated, all by itself, it works exactly how I expected it to. $Object contains all of the data from each part. but as soon as I put this at the top of my function that actually processes the report:

Function Read-Report
    {
    $Object = @()

    $FileName = Get-ChildItem $env:USERPROFILE\Downloads | where Extension -like .csv | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1

    $FileCount = [int]$FileName.basename.substring($FileName.Basename.Length-1)

    if ($FileCount -ne "1")
        {
        $List = Get-ChildItem $env:USERPROFILE\Downloads | where Extension -like .csv | Sort-Object -Property LastWriteTime -Descending | Select-Object -First $FileCount | Sort-object #([int]::Parse($filename.basename.substring($Filename.Basename.Length-1))) | Sort-Object
        Foreach($File in $List.FullName)
            {
            $Object += Import-Csv -Path $File
            }
        }
    Else
        {
        $Object = Import-Csv -Path $FileName.FullName
        }

#Working report processing script continues below

I get an error on the line that contains Foreach($File in $List.FullName) stating:

Cannot convert the "C:\Users\username\Downloads\Report_Part_1.csv" value of type "System.String" to type "System.Management.Automation.SwitchParameter".
At C:\Users\username\Documents\Scripting\Powershell\ReportsFunctions.ps1:43 char:17
+         Foreach($File in $List.FullName)
+                 ~~~~~
    + CategoryInfo          : InvalidArgument: (:) [], RuntimeException
    + FullyQualifiedErrorId : ConvertToFinalInvalidCastException

I would think that its just choking on the file path, except that it works when its not inside my function.

Also, changing the foreach loop to the following has the exact same error

Foreach($File in $List)
    {
    $Object += Import-Csv -Path $File.FullName
    }

Solution

  • I can reproduce your error with the following:

    function Read-Report
    {
        param( [switch] $File )
    
        foreach( $File in @( "aaa" ) )
        {
            write-host $File
        }
    }
    
    Read-Report
    

    What's happening is the function declares a type constrained variable with [switch] $File so that PowerShell requires $File to always contain a value of type [switch], but you're then using the same variable in a foreach which tries to iterate over an array of [string]s.

    Powershell's implicit type conversion tries to turn the [string] "aaa" into a [switch] but there's no implicit conversion that can do this so you get the error:

     Cannot convert the "aaa" value of type "System.String" to type "System.Management.Automation.SwitchParameter".
    

    (Note that [switch] is a Type Accelerator for [System.Management.Automation.SwitchParameter])

    Your code doesn't show where you're declaring $File so I can't suggest a direct fix, but a workaround would be to use a different variable in the foreach iterator so it doesn't have the same [switch] type constraint and then PowerShell won't try to convert the [string] "aaa" value into another type:

    function Read-Report
    {
        param( [switch] $File )
    
        foreach( $MyFile in @( "aaa" ) )
        {
            write-host $MyFile
        }
    }
    
    Read-Report