Getting positional errors for batch robocopy script that takes in array arguments for sources, destinations, and switches

Thank you for reading. I am trying to create a batch robocopy Powershell script so I don’t have to manually robocopy multiple folders, just pass in the parameters, however I’m getting positional based errors and am having trouble understanding what I did wrong.

The Powershell script:

# Use arguments and pass into command
param (
    [Parameter(Mandatory=$true)]
    [string[]]$sources,

    [Parameter(Mandatory=$true)]
    [string[]]$destinations,

    # Define robocopy options, if nothing's passed in as the parameter this is the default
    [Parameter(Mandatory=$false)]
    [string[]]$robocopyOptions = @('/MIR', '/COPYALL', '/DCOPY:DAT', '/R:3', '/W:5', '/LOG+:robocopy.log')
)

if ($sources.Count -eq 0) {
    Write-Host "Error: There are no source paths."
    exit
}

if ($destinations.Count -eq 0) {
    Write-Host "Error: There are no destination paths."
    exit
}

# Verify that the number of source and destination paths match
if ($sources.Count -ne $destinations.Count) {
    Write-Host "Error: Number of source paths does not match the number of destination paths."
    exit
}

# Loop through each source-destination pair and run robocopy
for ($i = 0; $i -lt $sources.Count; $i++) {
    $source = $sources[$i]
    $destination = $destinations[$i]

    # Check if source and destination paths exist
    if (-not (Test-Path $source)) {
        Write-Host "Source path '$source' does not exist. Skipping this iteration."
        continue
    }

    if (-not (Test-Path $destination)) {
        Write-Host "Destination path '$destination' does not exist. Skipping this iteration."
        continue
    }

    Write-Host "Copying from $source to $destination"

    # Run robocopy
    robocopy $source $destination $robocopyOptions

    Write-Host $source $destination $robocopyOptions
}

Write-Host "All copy operations completed."

First command I used to try to see if I can have multiple sources. I run this in cmd as administrator.

powershell.exe  -File "D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY.ps1" -sources "D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY A", "D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY C" -destinations "D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY B", "D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY D"

D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY.ps1 : A positional parameter cannot be found that accepts argument
'D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY C'.
    + CategoryInfo          : InvalidArgument: (:) [BATCH ROBOCOPY.ps1], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : PositionalParameterNotFound,BATCH ROBOCOPY.ps1

I thought it might have had something to do with me not writing the arguments correctly because I had spaces between my elements in the array?

powershell.exe -File "D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY.ps1" -sources "D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY A","D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY C" -destinations "D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY B","D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY D"
Source path 'D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY A,D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY C' does not exist. Skipping this iteration.
All copy operations completed.

A different command I tried without multiple sources and destinations and trying to pass in parameters for robocopyOptions, got the same positional error but for a different place.

powershell.exe  -File "D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY.ps1" -sources "D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY A" -destinations "D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY B" -robocopyOptions "/SEC", "/MOVE", "/E" 
D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY.ps1 : A positional parameter cannot be found that accepts argument '/MOVE,'.
    + CategoryInfo          : InvalidArgument: (:) [BATCH ROBOCOPY.ps1], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : PositionalParameterNotFound,BATCH ROBOCOPY.ps1

I looked at Powershell: A positional parameter cannot be found that accepts argument “xxx” but still don’t really understand the issue because I did put the parameter name?

I was also looking at Powershell with Robocopy and Arguments Passing - Stack Overflow too, and from my understanding, to pass in robocopy switches, the best way to do it is to make an array of separate values, and that’s why I made robocopyOptions an array.

Passing in only one source and one destination without passing in arguments for robocopyOptions works. So am I doing something wrong with my arrays? I don’t get what I could be doing wrong there because I thought you’re supposed to just separate the arguments by commas.

You’re calling Powershell.exe, but then passing it the parameters you have in your script. Powershell.exe does not accept those params, your script does. The only one that does work is -File.

Instead, just use PowerShell, and run the script with params from PS directly:

& '.\BATCH ROBOCOPY.ps1' -sources '.\Data\' -destinations '.\Test'

If you absolutely have to use powershell.exe to call it, you need to use the -Command parameter on powershell.exe so you can execute yourr command and parameters. take a look at the help doc for powershell .exe for help on that, it has some examples:

powershell.exe /h

Side note:

I’d review your code a bit, definitely some unnecessary stuff IMO.

  1. If you have a mandatory param, you really don’t need to check if the count is 0. by having it mandatory it means you have to have something there.
  2. Look into the ForEach-Object cmdlet. It’ll greatly improve the readability and overall flow instead of that for loop.
param (
    [Parameter(Mandatory=$true)]
    [hashtable]$sourceDestinationMap,

    # Define robocopy options, if nothing's passed in as the parameter this is the default
    [Parameter(Mandatory=$false)]
    [string[]]$robocopyOptions = @('/MIR', '/COPYALL', '/DCOPY:DAT', '/R:3', '/W:5', '/LOG+:robocopy.log')
)

# Verify that the number of source and destination paths match
if ($sourceDestinationMap.keys.Count -ne $sourceDestinationMap.values.Count) {
    Write-Host "Error: Number of source paths does not match the number of destination paths."
    exit
}

$sourceDestinationMap.GetEnumerator() | ForEach-Object {
    $source = $_.Key
    $destination = $_.Value

    if (-not (Test-Path $source)) {
        Write-Host "Source path '$($source)' does not exist. Skipping this iteration."
        continue
    }

    if (-not (Test-Path $destination)) {
        Write-Host "Destination path '$($destination)' does not exist. Skipping this iteration."
        continue
    }

    # Run robocopy
    robocopy $source $destination $robocopyOptions

    Write-Host  "`nROBOCOPY `"$($source)`" `"$($destination)`" $($robocopyOptions)`n"
}

Write-Host "All copy operations completed."

Tested with this and it’s working correctly
powershell.exe -Command "& '.\BATCH ROBOCOPY.ps1' -sourceDestinationMap @{'D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY A' = 'D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY B'; 'D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY C' = 'D:\OTHER\TỰ ĐỘNG HÓA\BATCH ROBOCOPY D'} -robocopyOptions '/SEC', '/MOVE', '/E'"

Thank you so much for explaining.

I’m trying to run this as a task schedule and am not sure how I’d do that without calling Powershell then? Because in the task you’d have to start a program from my understanding. Again I’m not really familiar much with it so my bad for not getting something obvious. Is it bad to call Powershell this way?

Not necessarily bad. It just depends on what you’re doing. For example, if this was just a script you were gonna run ad-hoc, i wouldn’t do it via powershell.exe, I’d just launch PS and run it. I actually think via Scheduled Task, this way makes sense, and in fact, is how I often call scripts/function as well, so I think you’re good there!

1 Like

Ok, thank you so much for your help, I appreciate it. Yea I’m making this a scheduled task to copy files in specific folders to specific folders every several months, automation baby. :slight_smile:

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.