Compo has provided an effective solution in a comment on the question, but let me dig deeper:
tl;dr
@echo off
:: No whitespace around "="
:: "..." around BOTH the name and the variable.
set "string=dGVzdA=="
:: Use -NoProfile -Command to call the PowerShell CLI
:: Pass the PowerShell code inside "..."
:: Refer to the value of the variable named string as %string%
:: Enclose %string% in '...'
:: "delims=" is enough - no need for "tokens=* delims="
for /f "delims=" %%i in ('
powershell -NoProfile -Command "[Text.Encoding]::Utf8.GetString([Convert]::FromBase64String('%string%'))"
') do set "decoded=%%i"
echo %decoded%
The immediate fix is twofold:
Taking a step back:
For conceptual clarity, avoid making "
chars. a literal part of variable values; therefore, based on the above:
:: Make the value of string verbatim dGVzdA==
:: Double-quote later, if needed.
set "string=dGVzdA=="
When calling powershell.exe
, the Windows PowerShell CLI, or pwsh
, the PowerShell (Core) CLI, it's best to:
enclose the PowerShell command(s) being passed to the (default) -Command
(-c
) parameter in "..."
overall, which (typically) prevents cmd.exe
from interpreting metacharacters such as &
and >
as its own.
precede -Command
/ -c
with -NoProfile
so as to avoid unnecessary overhead and to make the execution environment more predictable.
That is, generally use the following idiom:
powershell.exe -NoProfile -Command "..."
In order to nest strings inside the "..."
string passed to -Command
:
Use '...'
, if possible, which avoids escaping headaches.
If the string value itself contains '
, double such embedded instances; the following example automates this escaping with cmd.exe
's substring-substitution syntax, %var:'=''%
:
set "var=5 o'clock"
for /f "delims=" %%i in ('
powershell.exe -NoProfile -Command " Write-Output '%var:'=''%' "
') do echo [%%i]
If you do need to nest a "..."
string inside the overall "..."
-Command
argument - namely if you want to perform string interpolation on the PowerShell side - things get tricky - see next section.
Nesting "..."
strings inside powershell.exe -NoProfile -Command "..."
in a for /f
loop:
If you do need to nest a "..."
string inside the overall "..."
-Command
argument - namely if you want to perform string interpolation on the PowerShell side (too) - things get tricky:
Usually, the best choice is to use \"...\"
- it works with the CLIs of both PowerShell editions and also works when calling from no-shell contexts such as Task Scheduler and the Windows Run
dialog (WinKey-R); also, it is effectively cross-platform, given that Unix shells such as Bash use \
as the escape character too:
set "var=here and there"
:: -> [here and there]
for /f "delims=" %%i in ('
powershell.exe -NoProfile -Command " Write-Output \"%var%\" "
') do echo [%%i]
Note: If a value itself has embedded "
chars., "escape" them manually as $([char]34)
(sic; a PowerShell subexpression that yields a literal "
) or programmatically as %var:"=$([char]34)%
However, given that cmd.exe
doesn't recognize \"
as an escaped "
character, this breaks if the string value happens to contain cmd.exe
metacharacters such as &
:
set "var=here & there"
:: !! BREAKS, due to the "&" in the variable value.
for /f "delims=" %%i in ('
powershell.exe -NoProfile -Command " Write-Output \"%var:"=`\"%\" "
') do echo [%%i]
set "var=here & there"
:: OK, due to use of ""
:: -> [here & there]
for /f "delims=" %%i in ('
pwsh.exe -NoProfile -Command " Write-Output ""%var%"" "
') do echo [%%i]
Note: If value itself has embedded "
chars., escape them manually as `""
(sic) or programmatically as %var:"=`""%
set "var=here & there"
:: OK, due to use of \"", but whitespace is normalized
:: -> [here & there]
for /f "delims=" %%i in ('
powershell.exe -NoProfile -Command " Write-Output \""%var%\"" "
') do echo [%%i]
set "var=here & there"
:: OK, with whitespace preservation
:: -> [here & there]
for /f "delims=" %%i in ('
powershell.exe -NoProfile -Command " Write-Output " ^^^"\"%var%\"" "
') do echo [%%i]
Note: In both cases, if a value itself has embedded "
chars., "escape" them manually as $([char]34)
(sic; a PowerShell subexpression that yields a literal "
) or programmatically as %var:"=$([char]34)%