I need to run bash commands under Powershell using Cygwin's bash.
I faced the issue that Powershell 5 and 7 processes double quotes in different way.
I'd like to have bash script working in both. Can I set up both Powershell to make them processing quotes in the same way? Or another way to have export MYKEY="MYVALUE"
with double quotes in Cygwin?
Powershell 5:
PS D:\work> $PSVersionTable.PSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 22621 3958
PS D:\work> C:\cygwin64\bin\bash.exe -l -c 'key=MYKEY; value=MYVALUE; echo export $key="$value";'
export MYKEY=MYVALUE
PS D:\work> C:\cygwin64\bin\bash.exe -l -c 'key=MYKEY; value=MYVALUE; echo export $key=\"$value\";'
export MYKEY=MYVALUE
PS D:\work> C:\cygwin64\bin\bash.exe -l -c 'key=MYKEY; value=MYVALUE; echo export $key=\\\"$value\\\";'
export MYKEY="MYVALUE"
Powershell 7:
PS D:\work> $PSVersionTable.PSVersion
Major Minor Patch PreReleaseLabel BuildLabel
----- ----- ----- --------------- ----------
7 4 4
PS D:\work> C:\cygwin64\bin\bash.exe -l -c 'key=MYKEY; value=MYVALUE; echo export $key="$value";'
export MYKEY=MYVALUE
PS D:\work> C:\cygwin64\bin\bash.exe -l -c 'key=MYKEY; value=MYVALUE; echo export $key=\"$value\";'
export MYKEY="MYVALUE"
PS D:\work> C:\cygwin64\bin\bash.exe -l -c 'key=MYKEY; value=MYVALUE; echo export $key=\\\"$value\\\";'
export MYKEY=\"MYVALUE\"
It is PowerShell (Core) 7's behavior that is correct, specifically in v7.3+, which corrected a long-standing bug that still affects Windows PowerShell - see this answer for details.
You can instruct PowerShell 7.3+ to exhibit the old, broken behavior via the $PSNativeCommandArgumentPassing
preference variable, by setting it to 'Legacy'
:
Therefore, the following snippet makes the bash.exe
call exhibit the same behavior in both PowerShell editions:
if ($IsCoreCLR) { $PSNativeCommandArgumentPassing = 'Legacy' }
C:\cygwin64\bin\bash.exe -l -c 'key=MYKEY; value=MYVALUE; echo export $key=\\\"$value\\\";'
# Alternatively, with a specific version check, which also avoids
# strict-mode problems, given that the $IsCoreCLR variable isn't defined
# in Windows PowerShell:
if ($PSVersionTable.PSVersion -ge '7.3') { $PSNativeCommandArgumentPassing = 'Legacy' }
C:\cygwin64\bin\bash.exe -l -c 'key=MYKEY; value=MYVALUE; echo export $key=\\\"$value\\\";'
Note:
You may choose to simply unconditionally set $PSNativeCommandArgumentPassing = 'Legacy'
, given that Windows PowerShell will just ignore it.
If you want to scope the effect of setting $PSNativeCommandArgumentPassing
, so as to limit it to specific commands, you can enclose the latter in a script block ({ ... }
) and invoke that with &
, the call operator, which runs the enclosed code in a child scope, so that a scope-local copy of $PSNativeCommandArgumentPassing
is created, without affecting the caller's copy.
# Enclosure in & { ... } scopes the effect of setting
# $PSNativeCommandArgumentPassing
# The caller's original value remains in effect after execution
# of the script block.
& {
$PSNativeCommandArgumentPassing = 'Legacy'
C:\cygwin64\bin\bash.exe -l -c 'key=MYKEY; value=MYVALUE; echo export $key=\\\"$value\\\";'
}