Docker cp parameter expansion in powershell

1k views Asked by At

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
2

There are 2 answers

3
HAL9256 On BEST ANSWER

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"

0
nischay goyal On

The way you defined it, docker cp is actually considering $cp_arguments as 1 string and that's why it's complaining about 2 arguments Have a look below

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

And if you try to run it in this fashion it will fail.

Try the below approach in which you don't pass "" to $cp_arguments

docker cp $cp_arguments

You have already defined another way in the question which is a bit hacky butone more approaches would be.

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

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