How to get the Decoded string value using powershell statement from batch script? - powershell

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 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]
In PowerShell (Core) 7+, whose CLI is pwsh.exe, using ""..."" is a simple and robust solution:
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)%

Related

How to use a PowerShell function return as a variable in a batch file

I was trying to use the return from myPowershellScript.ps1 to use as a variable in my batch file.
myPowershellScript.ps1
function GetLatestText
{
return "Hello World"
}
I was trying to use the For /F function. There may be a better way.
myBatch.bat
for /f "delims=" %%a in (' powershell -command "\\Rossi2\Shared\myPowershellScript.ps1" ') do set "var=%%a"
echo %var%
Desired output, would be to have 'Hello World' output in the cmd window.
I was trying to use the batch file as some old processes use them. For newer processes I do everything in PowerShell and it works fine.
The current output is blank.
Your syntax for trying to capture output from a PowerShell script from a batch file is correct (assuming single-line output from the script),[1] except that it it is more robust to use the -File parameter of powershell.exe, the Windows PowerShell CLI than the -Command parameter.
See this answer for when to use -File vs. -Command.
Your problem is with the PowerShell script itself:
You're defining function Get-LatestText, but you're not calling it, so your script produces no output.
There are three possible solutions:
Place an explicit call to Get-LatestText after the function definition; if you want to pass any arguments received by the script through, use Get-LatestText #args
Don't define a function at all, and make the function body the script body.
If your script contains multiple functions, and you want to call one of them, selectively: in your PowerShell CLI call, dot-source the script file (. <script>), and invoke the function afterwards (this does require -Command):
for /f "delims=" %%a in (' powershell -Command ". \"\\Rossi2\Shared\myPowershellScript.ps1\"; Get-LatestText" ') do set "var=%%a"
echo %var%
[1] for /f loops over a command's output line by line (ignoring empty lines), so with multiline output only the last line would be stored in %var% - more effort is needed to handle multiline output.
You can combine the batch and the powershell in single file (save this as .bat ):
<# : batch portion
#echo off & setlocal
for /f "tokens=*" %%a in ('powershell -noprofile "iex (${%~f0} | out-string)"') do set "result=%%a"
echo PS RESULT: %result%
endlocal
goto :EOF
: end batch / begin powershell #>
function GetLatestText
{
return "Hello World"
}
write-host GetLatestText

How to pass a string array to powershell through vbscript?

I'm trying to pass a string array to powershell through vbscript.
I got the params in the file.ps1 script:
param (
[string[]]$target
)
I'm trying to call file.ps1 through vbscript.
I want $target = #('c:\dev','d:\lib') to be passed in.
I currently have in my vbscript :-
target1 = """c:\dev"", ""d:\lib"""
Set objShell = CreateObject("Wscript.Shell")
objShell.run("powershell.exe -noexit -ExecutionPolicy Unrestricted -file .\file.ps1 -target1 """ & target & """")
which returns:
c:\dev, d:\lib and which isn't the powershell string array format "c:\dev", "d:\lib"
Update - Thanks mklement0 and alex-dl for your answers!
alex-dl's helpful answer contains an effective solution, as long as the array's elements don't need quoting, but let me try to break it down conceptually in more detail:
The -File parameter of PowerShell's CLI, powershell.exe (pwsh in PowerShell (Core) 7+) fundamentally does not support passing arrays to PowerShell code, because all arguments are interpreted strictly as whitespace-separated, with verbatim content.
E.g., "c:\dev", "d:\lib" on the command line is parsed as two arguments:
c:\dev, (note the trailing ,, syntactic " quotes stripped)
d:\lib
You must use the -Command (-c) option in order to pass arrays.
-Command fundamentally changes how the arguments are parsed:
All arguments are stripped of syntactic (non \"-escaped) " quotes.
The resulting tokens are space-concatenated to form a single string.
The resulting string is then interpreted as PowerShell code, i.e. as if you had submitted it from inside a PowerShell session.
This therefore enables all of PowerShell's features, such as passing arrays with ,, '...' (single-quoting), ..., and notably also means that references to PowerShell variables (e.g. $HOME) are recognized (unlike with -File).
See this answer for more guidance on when to use -File vs. -Command (-c).
Therefore, the best approach in your case is:
target = "'c:\dev', 'd:\lib'" ' Note the embedded '...'-quoting
Set objShell = CreateObject("Wscript.Shell")
' Note the use of -c (-command) instead of -file
objShell.Run("powershell.exe -noexit -ExecutionPolicy Unrestricted -c .\file.ps1 -target " & target)
Using embedded '...'-quoting simplifies passing the array elements in a way that PowerShell sees them as individually quoted (with the values at hand, this isn't strictly necessary, but it may be in other cases).
Doing this with embedded "..."-quoting gets unwieldy, because each embedded " must then be escaped as \"" (sic):
"" to escape the " inside the VBScript string,
and \ to ensure that \" is ultimately passed on the command line, which PowerShell requires for escaping " on the command line[1] (whereas PowerShell-internally, it is `" or (alternatively, inside a double-quoted string) "").
target = "\""c:\dev\"", \""d:\lib\"""
Set objShell = CreateObject("Wscript.Shell")
objShell.Run("powershell.exe -noexit -ExecutionPolicy Unrestricted -c .\file.ps1 -target " & target)
[1] While in Windows PowerShell you can situationally get away with "" instead of \", it can break, especially if the overall -c argument is enclosed in "...".
This problem has been fixed in PowerShell (Core) 7+, where you can now use "" and \" interchangeably.
E.g., when invoked from cmd.exe,
powershell -c " ""ab c"".length " breaks, whereas
pwsh -c " ""ab c"".length " works.
You could use the following vbscript:
target1 = """c:\dev, d:\lib"""
Set objShell = CreateObject("Wscript.Shell")
objShell.run("powershell.exe -Command .\file.ps1 -target " & target1)
And this powershell script:
param (
[string[]]$target
)
foreach ($t in $target) {
write-output $t
}
Read-Host -Prompt "Press Enter to exit"
Without -Command the argument is always interpreted as a single string.

Double quotes not escaping from string when calling Powershell command from within Batch script?

Double quotes structure is not retained in my test message when passed through a powershell instance called through a batch script, as detailed below:
set test={"this":"is","a":"test"}
FOR /F "delims=" %%i in (' powershell -Command "& {$message = '%test%'; echo $message}" ') DO SET message=%%i
echo %test%
echo %message%
the output is as follows:
{"this":"is","a":"test"}
{this:is,a:test}
I would like to retain the quotes to further process the string in powershell, but as you can see, they are stripped when introduced into the $message variable.
Any insight as to how I might fix this?
Echoing %test% containing double quotes inside the d-quoted powershell command will break the enclosing d-quotes.
One way to overcome this, is using batch string substitution to escape the inner d-quotes with a backslash on the fly
:: Q:\Test\2019\04\25\SO_55855412.cmd
#Echo off
set "test={"this":"is","a":"test"}"
FOR /F "delims=" %%i in ('
powershell -Command "& {$message = '%test:"=\"%'; echo $message}"
') DO SET "message=%%i"
echo %test%
echo %message%
returns here:
> SO_55855412.cmd
{"this":"is","a":"test"}
{"this":"is","a":"test"}
Another solution is to NOT pass %test% as an argument, but to get the variable from the inherited environment
powershell -Command "& {$message = $env:test; echo $message}"
Sadly this doesn't work to pass a variable back, as on terminating the spawned app the inherited environment is discarded.

How to replace the given line of text file in looping structure using windows batch file

set instance=3
for /L %%A in (1,1,%instance%) do (
set /p var=LogfileName: trial.txt
set rep=LogfileName: logfile.txt
echo %var%
powershell -Command "(gc InputFile.txt) -replace '%var%', '%rep%' | Out-File InputFile.txt"
)
I am not able to replace the line in text file using this commands. And also I am not getting how to take the line number and store the string in that particular lile in file into a batch file variable.
% variables in batch files are expanded at parse time (when the statement is read). Since the entire for statement, including the action block, is read as one statement, %var% and %rep% are expanded before the loop is executed, meaning they evaluate to empty strings.
To have the variables inside the loop expanded at execution time you need to enable delayed expansion and use ! notation:
setlocal EnableDelayedExpansion
set instance=3
for /L %%A in (1,1,%instance%) do (
set /p var=LogfileName: trial.txt
set rep=LogfileName: logfile.txt
echo !var!
powershell -Command "(gc InputFile.txt) -replace '!var!', '!rep!' | Out-File InputFile.txt"
)
For further explanation see Raymond Chen's blog.

How to use line continuation inside quoted string of FOR /F

I would like to be able to specify multiple lines for the command of a FOR /F loop. The first three (3) commands work as expected. However, the fourth (4th) command will never let me make a newline while inside the quoted command. I have tried the cmd.exe line continuation character caret, the PowerShell line continuation character backtick, and combined caret+backtick. It seems to simply skip over the fourth (4th) FOR /F loop without error or message.
Yes, I see that the fourth (4th) FOR /F loop is not in danger of overrunning the right side of the display. I am thinking of times when I have much longer commands. Is creating a .ps1 file the only answer? Can line continuation be made to work?
C:>type gd.bat
#ECHO OFF
FOR /F "usebackq tokens=*" %%d IN (`powershell -NoProfile -Command "get-date -format s"`) DO (SET "DT_STAMP=%%d")
ECHO DT_STAMP 1 is %DT_STAMP%
FOR /F %%d IN ('powershell -NoProfile -Command "Get-Date -Format s"') DO (SET "DT_STAMP=%%d")
ECHO DT_STAMP 2 is %DT_STAMP%
FOR /F %%d IN ('powershell -NoProfile -Command ^
"Get-Date -Format s"') DO (SET "DT_STAMP=%%d")
ECHO DT_STAMP 3 is %DT_STAMP%
FOR /F %%d IN ('powershell -NoProfile -Command ^
"Get-Date ^`
-Format s"') DO (SET "DT_STAMP=%%d")
ECHO DT_STAMP 4 is %DT_STAMP%
19:53:02.34 C:\src\t
C:>gd.bat
DT_STAMP 1 is 2018-01-07T19:53:10
DT_STAMP 2 is 2018-01-07T19:53:10
DT_STAMP 3 is 2018-01-07T19:53:10
20:01:39.37 C:\src\t
C:>echo %ERRORLEVEL%
0
You are attempting to do cmd.exe line continuation. The ^ has no special meaning if quoting is ON. You can escape the " as ^" so that the ^ line continuation works. You must also escape the second quote, else the closing ) will not be recognized.
FOR /F %%d IN ('powershell -NoProfile -Command ^
^"Get-Date ^
-Format s^"') DO (SET "DT_STAMP=%%d")
Note that the line breaks are purely cosmetic - Powershell still receives the entire command as a single line (without any line breaks).