powershelldockerdocker-for-windowsdocker-cp

Docker cp parameter expansion in powershell


I would like to run something like this:

$docker_container_name = "iar_build_container"
...
$cp_arguments =  $docker_container_name + ":C:/docker_work/IAR/Debug " + $docker_artifacts + "/Debug"

"Copying out the build artifacts: $cp_arguments"
docker cp "$cp_arguments"

The output of this is:

Copying out the build artifacts: iar_build_container:C:/docker_work/IAR/Debug ./docker_artifacts/Debug
"docker cp" requires exactly 2 arguments.
See 'docker cp --help'.

Usage:  docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
        docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH

Copy files/folders between a container and the local filesystem

If I hard code the docker cp command it works, and if I use the following it works:

docker cp iar_build_container:C:/docker_work/IAR/Debug $docker_artifacts/Debug

So I am having an issue expanding what would be the first parameter that is the container name and colon.

EDIT: This works, but it feels hacky:

$cp_arguments = "cp " + $docker_container_name + ":C:/docker_work/IAR/Debug " + `
                        $docker_artifacts + "/Debug"

"Copying out the build artifacts: $cp_arguments"
Start-Process -FilePath "docker" -ArgumentList "$cp_arguments" -Wait

Solution

  • To expand on @nishaygoyal answer. Running executables (e.g. docker) in PowerShell is different than running executables in CMD prompt. In PowerShell, arguments are passed as an array of strings, and not as a space separated series of strings like in CMD prompt. Hence by passing the space separated string of arguments, PowerShell is interpreting it as a single argument, and a single string.

    Hence, by simply changing your arguments into an array of strings, and moving the "cp" as one of those items. Things will work:

    $docker_container_name = "iar_build_container"
    $docker_artifacts = "./docker_artifacts"
    
    $cp_arguments = @("cp",                                               `
                      "$($docker_container_name):C:/docker_work/IAR/Debug",  `
                      "$docker_artifacts/Debug")
    
    docker $cp_arguments
    

    EDIT:

    As @Nick pointed out, we have to use the subexpression operator: $($docker_container_name) in the string to do proper string expansion because PowerShell will interpret $docker_container_name:C: as a variable instead of $docker_container_name. To PowerShell, the colon indicates the scope of the variable, e.g. $global:foo. So we need to use the subexpression operator $() to properly define our variable for string expansion.


    Why does using Start-Process like this work?

    $cp_arguments = "cp " + $docker_container_name + ":C:/docker_work/IAR/Debug " + `
                            $docker_artifacts + "/Debug"
    
    Start-Process -FilePath "docker" -ArgumentList "$cp_arguments" -Wait
    

    Well, according to Start-Process it's special in that the -ArgumentList can accept a space separated list of arguments, and it treats them in a CMD prompt style way.

    We also can use EchoArgs to see exactly what is being passed as arguments:

    $docker_container_name = "iar_build_container"
    $docker_artifacts = "./docker_artifacts"
    
    #Original:
    
    $cp_arguments =  $docker_container_name + ":C:/docker_work/IAR/Debug " + $docker_artifacts + "/Debug"
    
    PS C:\> EchoArgs.exe docker cp "$cp_arguments"
    Arg 0 is <docker>
    Arg 1 is <cp>
    Arg 2 is <iar_build_container:C:/docker_work/IAR/Debug ./docker_artifacts/Debug>
    
    Command line:
    "C:\ProgramData\chocolatey\lib\echoargs\tools\EchoArgs.exe" docker cp "iar_build_container:C:/docker_work/IAR/Debug ./docker_artifacts/Debug"
    

    (Notice that we are passing 2 arguments, cp, and the rest of the string. vs. passing an array:

    $cp_arguments = @("cp",                                               `
                      "$($docker_container_name):C:/docker_work/IAR/Debug",  `
                      "$docker_artifacts/Debug")
    
    PS C:\> EchoArgs.exe docker $cp_arguments
    Arg 0 is <docker>
    Arg 1 is <cp>
    Arg 2 is <iar_build_container:C:/docker_work/IAR/Debug>
    Arg 3 is <./docker_artifacts/Debug>
    
    Command line:
    "C:\ProgramData\chocolatey\lib\echoargs\tools\EchoArgs.exe" docker cp iar_build_container:C:/docker_work/IAR/Debug ./docker_artifacts/Debug
    

    In this case, you can see it splits out the arguments "correctly"