'kubectl patch' works on Linux Bash but not in Windows Powershell ISE

吃可爱长大的小学妹 提交于 2019-12-01 11:32:49

For detailed and very useful background, see the answer by mklement0

After much frustration, I have decided to list all variants of quote escaping that I've tried, and came up with one more, which suddenly worked! So, sharing it here:

kubectl patch deployment wapi-backend-d1 --patch '{\"spec\": {\"template\": {\"metadata\": {\"labels\": {\"date\": \"test123\"}}}}}'

This is how to use kubectl patch with Powershell

Also, of note: I was actually trying to patch it with a timestamp to trigger a rolling update without changing tags of container images (so set image would not help me).

When you try to put your JSON into a variable and then call kubectl patch with a variable, you get into trouble with escaping again. This is what I ended up with:

$patchRequest = @{
    spec = @{
        template = @{
            metadata = @{
                labels = @{
                    date = ((((Get-Date -Format o)).replace(':','-').replace('+','_')))
                }
            }
        }
    }
}
$patchJson = ((ConvertTo-Json -InputObject $patchRequest -Compress -Depth 10))
$patchJson = $patchJson.replace('"','\"')
kubectl patch deployment wapi-backend-d1 --patch $patchJson

You've found the right solution in your own answer, but let me try to break it down conceptually:

Embedding " (double quotes) in string arguments passed to external programs:

  • (a) First - sensibly and unavoidably - you need to satisfy PowerShell's syntax requirements with respect to embedding " chars. in quoted strings.

  • (b) Then - and this step shouldn't be necessary - you need to \-escape embedded " chars. that you want external programs to see.

    • This is a longstanding, irksome bug that is unlikely to be fixed, however, as long as backward compatibility must be maintained; see this GitHub issue.

Re (a), you have the following options:

  • '...'-quoting (single-quoting), inside of which you can use " as-is:

    • '{ "spec": "none" }'
    • Everything inside '...' is taken literally - no expansion (interpolation) takes place.
  • "..."-quoting (double-quoting), inside of which you can use `" or "" to embed " chars:

    • "{ `"spec`": `"none`" }" - ` is PowerShell's general escape char.
    • "{ ""spec"": ""none"" }" - "-specific escaping (doubling)
    • The content of "..." is subject to expansion (interpolation), meaning that you can reference variables ($var) or subexpressions ($(1 + 2)) inside such strings, which PowerShell replaces with their values - see this answer for more about PowerShell's expandable strings.

If you're passing such a string to other PowerShell commands (cmdlets, functions, or scripts), no further action is needed; e.g.:

PS> Write-Output '3" of rain'
3" of rain

Re (b) - i.e. to pass such strings to external programs - you additionally need to \-escape the embedded " chars.:

  • Applying manual escaping to the examples above:

    • '{ \"spec\": \"none\" }'
    • "{ \`"spec\`": \`"none\`" }"
    • "{ \""spec\"": \""none\"" }"
  • Applying the escaping programmatically to a preexisting string:

    • $str = '3" of rain'; $escapedStr = $str -replace '"', '\"'
  • That is, for an external program to ultimately see literal value 3" of rain, you must pass literal value 3\" of rain from PowerShell. This \-escaping is something that PowerShell, as a shell, should do automatically behind the scenes, but currently doesn't.

  • There's an additional bug in Windows PowerShell - since fixed in PowerShell Core - that mishandles strings with unbalanced embedded " chars. if a " is part of the first word:

    • E.g., the above techniques do NOT work with literal values such as 3" of rain - instead, you must use the following monstrosity: `"3\`" of rain`", which is technically a series of separate, unquoted arguments, which means that (a) multiple spaces between the words of the strings aren't supported (they are collapsed to a single space) and (b) PowerShell metacharacters such as & < > $ & | @ { must be individually `-escaped.
    • Note that the bug surfaces only if the " is part of the first word in the value, and only if that first word is not preceded by whitespace (though arguments with leading whitespace are rarely useful); e.g., '3 \" of rain' would again work, because the unbalanced " is not part of the first word.

E.g.:

# Using choice.exe as a sample external program to pass a string to.
# choice.exe echoes the string it receives via /m as the prompt message.
PS> choice /m '{ \"spec\": \"none\" }' /d Y /t 0
{ "spec": "none" } [Y,N]?Y
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!