2

I want to decode the encoded password value using powershell, and store the decoded value in a batch script variable,

I have used the following statement to encode the password in batch script using powershell statement. It is working fine and returning the value to batch script

@echo off

Set string ="test"

for /f "tokens=* delims=" %%i in ('powershell [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("""string"""^)^)') do set "encoded=%%i"

echo %encoded%

Output: dAB1AHMAdAA=

I have tried to decode this encoded value using following statement, but it is not returning any value to batch script

@echo off

Set string = "dAB1AHMAdAA="

for /f "tokens=* delims=" %%i in ('powershell [Text.Encoding]::Utf8.GetString([Convert]::FromBase64String("""string"""^)^)') do set "decoded=%%i"

echo %decoded%

Output: echo off

It should return test but som how no value is returned

If I manually execute the powershell statement in Windows Command Prompt, it is returning the value test.

powershell [Text.Encoding]::Utf8.GetString([Convert]::FromBase64String('dAB1AHMAdAA='))

But through batch script it is not returning and it is not giving any error also. I am beginner in batch script, Please anyone advise what I am doing wrong

Compo
  • 36,585
  • 5
  • 27
  • 39
  • 2
    Change ```Set string = "dAB1AHMAdAA="``` to ```Set "string=dAB1AHMAdAA="```, and ```'powershell [Text.Encoding]::Utf8.GetString([Convert]::FromBase64String("""string"""^)^)'``` to ```'powershell "[Text.Encoding]::Utf8.GetString([Convert]::FromBase64String('%string%'))"'```. – Compo Jan 02 '23 at 14:44

1 Answers1

0

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:

  • set string = "dAB1AHMAdAA=" -> set string="dAB1AHMAdAA"

    • In a set command, = mustn't have whitespace on either side:

      • If there is, it becomes part of the variable name and/or value.
    • Also, " chars. on the RHS of = become a literal part of the value.

      • To avoid that, omit the " chars. or, to allow cmd.exe metacharacters to be used in values, enclose the name, =, and the value as a whole in "..."; e.g.:

        • set "string=a & b | c"
    • Also, "tokens=* delims=" is redundant in that "delims=" is enough for for /f to read each line output by the specified command into a single variable (%%i in this case).

  • """string""" -> """%string%"""

    • That is, you must enclose a cmd.exe variable name in %...% in order to expand it to its value.

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:"=`""%

  • In Windows PowerShell, things are more complicated:

    • Using \""...\"" works safely, but in effect normalizes whitespace: that is, runs of two or more spaces become a single space each.
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]
  • In the rare event that you must prevent whitespace normalization, the solution gets ugly:

    • Use " ^^^"\"...\"" (sic)

      • Note: Outside for /f, the - still obscure - "^""..."^"" is enough.
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)%

mklement0
  • 382,024
  • 64
  • 607
  • 775