11

I'm trying to write a script that uses robocopy. If I were just doing this manually, my command would be:

robocopy c:\hold\test1 c:\hold\test2 test.txt /NJH /NJS

BUT, when I do this from powershell, like:

$source = "C:\hold\first test"
$destination = "C:\hold\second test"
$robocopyOptions = " /NJH /NJS "
$fileList = "test.txt"

robocopy $source $destination $fileLIst $robocopyOptions

I get:

-------------------------------------------------------------------------------
   ROBOCOPY     ::     Robust File Copy for Windows
-------------------------------------------------------------------------------

  Started : Fri Apr 10 09:20:03 2015

   Source - C:\hold\first test\
     Dest - C:\hold\second test\

    Files : test.txt

  Options : /COPY:DAT /R:1000000 /W:30

------------------------------------------------------------------------------

ERROR : Invalid Parameter #4 : " /NJH /NJS "

However, if I change the robocopy command to

robocopy $source $destination $fileLIst  /NJH /NJS 

everything runs successfully.

So, my question is, how can I pass a string as my robocopy command options (and, in a larger sense, do the same for any given external command)

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
weloytty
  • 5,808
  • 5
  • 28
  • 35

4 Answers4

19

Start robocopy -args "$source $destination $fileLIst $robocopyOptions"
or
robocopy $source $destination $fileLIst $robocopyOptions.split(' ')

Anton Z
  • 270
  • 2
  • 6
12

Use the arrays, Luke. If you specify an array of values, PowerShell will automatically expand them into separate parameters. In my experience, this is the most reliable method. And it doesn't require you to mess with the Start-Process cmdlet, which is in my opinion is overkill for such tasks.

This trick is from the best article I've seen on the PowerShell behavior towards external executables: PowerShell and external commands done right.

Example:

$source = 'C:\hold\first test'
$destination = 'C:\hold\second test'
$robocopyOptions = @('/NJH', '/NJS')
$fileList = 'test.txt'

$CmdLine = @($source, $destination, $fileList) + $robocopyOptions
& 'robocopy.exe' $CmdLine
beatcracker
  • 6,714
  • 1
  • 18
  • 41
6

You can't use a string to pass options in that way because when you write

robocopy $source $destination $fileList $robocopyOptions

PowerShell will evaluate the last variable ($robocopyOptions) as a single string and it will quote it. This means robocopy will get "/NJH /NHS" (single string, quoted) on its command line. (Obviously not the intent.)

For details on how to work around these kinds of issues, see here:

http://windowsitpro.com/powershell/running-executables-powershell

The article includes the following function:

function Start-Executable {
  param(
    [String] $FilePath,
    [String[]] $ArgumentList
  )
  $OFS = " "
  $process = New-Object System.Diagnostics.Process
  $process.StartInfo.FileName = $FilePath
  $process.StartInfo.Arguments = $ArgumentList
  $process.StartInfo.UseShellExecute = $false
  $process.StartInfo.RedirectStandardOutput = $true
  if ( $process.Start() ) {
    $output = $process.StandardOutput.ReadToEnd() `
      -replace "\r\n$",""
    if ( $output ) {
      if ( $output.Contains("`r`n") ) {
        $output -split "`r`n"
      }
      elseif ( $output.Contains("`n") ) {
        $output -split "`n"
      }
      else {
        $output
      }
    }
    $process.WaitForExit()
    & "$Env:SystemRoot\system32\cmd.exe" `
      /c exit $process.ExitCode
  }
}

This function will let you run an executable in the current console window and also let you build an array of string parameters to pass to it.

So in your case you could use this function something like this:

Start-Executable robocopy.exe $source,$destination,$fileList,$robocopyOptions
Bill_Stewart
  • 22,916
  • 4
  • 51
  • 62
1

Putting the options in separate arguments worked for me. Using Robocopy for copying excluding any CSV files.

$roboCopyPath = $env:ROBOCOPY_PATH
$otherLogsPath = [System.IO.Path]::Combine($basePath, "Logs-Other")
$atrTestResults = [System.IO.Path]::Combine($Release, $BuildNumber)
$ResultsSummary = [System.IO.Path]::Combine($basePath, "Result")

$robocopyOptions = @("/log:$otherLogsPath\robocopy.log", '/xf', '*.csv')
$CmdLine = @($atrTestResults, $ResultsSummary) + $robocopyOptions 
&$roboCopyPath $CmdLine
noksadayu
  • 11
  • 1