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

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


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 present up to at least PowerShell 7.1, which may get fixed in 7.2 - see this answer.

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:

    • Replace verbatim " with verbatim \", as well as any preexisting, immediately preceding \ with \\:

      • $str = '3" of rain'; $escapedStr = $str -replace '([\\]*)"', '$1$1\"'

      • That is, for an external program to ultimately see value 3" of rain verbatim, 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; that is, escaping this as '3\" of rain' does not work as expected - 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.

Example:

The following uses choice.exe as an example external program, because it can be repurposed (via options /d Y /t 0) to merely echo the prompt string it is given, which shows how it received the string passed from PowerShell:

& {  # Note: For preview versions of v7.2, deactivate the experimental  #       feature that fixes the problem, so as to show the original problem.     $PSNativeCommandArgumentPassing = 'Legacy'  # Use manual \-escaping to pass what should be received as  # verbatim { "spec": "none" } to an external program.  choice /m '{ \"spec\": \"none\" }' /d Y /t 0}

The above outputs { "spec": "none" } [Y,N]?Y, showing that the manually escaped " chars. were received as verbatim " chars. by the external program.