0
votes

I need to call a powershell script from another powershell script. The powershell script in question, myrobocopy.ps1, is a generalised module, just tuned to our specific needs. I need to call this from another script and get the error code it returns.

Attempts:

I tried it with Invoke-Expression, like this:

$source = "C:/sourcefolder"
$destination = "C:/destinationfolder"
$filename = "filename /LOG+:log.txt"
$arguments = "-source $source -destination $destination -file $filename"
$robocopyscript = "myrobocopy.ps1"
Invoke-Expression "$robocopyscript $arguments"
    if ($LASTEXITCODE -ne 1){
        $problems = 1
    }

I think the issue with this is that we can't force the process to wait until myrobocopy.ps1 is finished. Additionally, if I'm honest with you, I don't even think $LASTEXITCODE works here, since Invoke-Expression isn't some global error handling tool. I'm new to Powershell, so yeah, its dumb, just showing my work here.

Next attempt looked like this:

$robocopy = "C:\testfolder\myrobocopy.ps1"
$source = "C:\testfolder"
$destination = "C:\testdestination"
$filename = "test.bat"
$arguments = "-source $source -destination $destination -file $filename"
$p = Start-Process $robocopy $arguments -Passthru -Wait
$code = $p.ExitCode

For all I know, this should work, yet when this script runs, it just opens myrobocopy.ps1 in notepad. I've seen some other answers regarding how to associate ps1 files with powershell itself, but I don't believe (could be mistaken here) that is the issue here.

Finally, I read that I need to actually start powershell itself, and then call the ps script. I'm not really sure how to do that, so I tried the following:

$robocopy = "C:\testfolder\myrobocopy.ps1"
$source = "C:\testfolder"
$destination = "C:\testdestination"
$filename = "test.bat"
$arguments = "-source $source -destination $destination -file $filename"
$p = Start-Process "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -File $robocopy -ArgumentList $arguments -PassThru -Wait
$code = $p.ExitCode

This returns the following error: Start-Process : A positional parameter cannot be found that accepts argument 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe'

So yeah I'm slightly lost. I'm sure I just don't know the syntax on how to do it. Help would be greatly appreciated, thank you!

EDIT: Just to add more info, myrobocopy.ps1 accepts 3 string parameters and explicitly returns the $LASTEXITCODE after running robocopy.

ANSWER:

Using the call operator made this work, and you use $LASTEXITCODE to retrieve the exit code.

$robocopy = "C:\testfolder\myrobocopy.ps1"
$source = "C:\testfolder"
$destination = "C:\testdestination"
$filename = "test.bat"
& $robocopy $source $destination $filename
Write-Output $LASTEXITCODE
2
scripts don't usually have exit codes - binaries do. Does myrobocopy.ps1 return the $LASTEXITCODE after running robocopy? - Mathias R. Jessen
@MathiasR.Jessen Yes it does. I'm going to update my original post to reflect this. Thank you for helping make my question clearer. - Yvain

2 Answers

2
votes

If your "C:\testfolder\myrobocopy.ps1" put the result code on the pipeline, the call operator should do the trick:

$result = & "C:\testfolder\myrobocopy.ps1"
0
votes

Another way to run robocopy and get the correct exit code would be to use Invoke-Expression with $global:LASTEXITCODE:

Function Start-Robocopy {
    Param (
        [Parameter(Mandatory)]
        [String]$Source,
        [Parameter(Mandatory)]
        [String]$Destination,
        [Parameter(Mandatory)]
        [String]$Switches,
        [String]$File
    )

    Try {
        $result = [PSCustomObject]@{
            Source         = $Source
            Destination    = $Destination
            File           = $File
            Switches       = $Switches
            RobocopyOutput = $null
            ExitCode       = $null
            Error          = $null
        }
        
        $global:LASTEXITCODE = 0 # required to get the correct exit code
        
        $expression = [String]::Format(
            'ROBOCOPY "{0}" "{1}" {2} {3}', 
            $Source, $Destination, $File, $Switches
        )
        $result.RobocopyOutput = Invoke-Expression $expression
        $result.ExitCode = $LASTEXITCODE
    }
    Catch {
        $result.Error = $_
    }
    Finally {
        $result
    }
}

$startParams = @{
    Source      = '\\contoso\FolderA'
    Destination = '\\contoso\FolderA' 
    Switches    = '/MIR /Z /R:3 /W:10 /NP /MT:16'
}
Start-Robocopy @startParams