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

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.

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 get the Decoded string value using powershell statement from batch script?

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)%

Getting a variable from a powershell script, in a batch file

I have seen some similar questions on this here on stack overflow, but I cannot get any of the answers to far to work.
I have this .ps1 file that mounts a drive and echos the drive letter (expected $driverLetter = "G" || "H" || "I"):
$mountDisk = Mount-DiskImage -ImagePath $args[0] -Passthru
$driveLetter = ($mountDisk | Get-Volume).DriveLetter
echo $driveLetter
I'm running it from this batch file:
FOR /F "usebackq delims=" %%i IN (`powershell -File ./mountDisk.ps1 "%1"`) DO SET "d=%%i"
Echo %d%
Each time I get an empty variable. I've tried setting environment variables, but yield same result.
Here's how I'd probably do it, assuming that the initial path passed to the batch file is double-quoted as necessary.
#Echo Off & SetLocal EnableExtensions & Set "ISODrv="
If /I Not "%~x1" == ".iso" (Exit /B 1) Else For %%G In ("%~1") Do If "%%~aG" GEq "d" Exit /B 2
For /F %%G In ('%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command "(Mount-DiskImage -ImagePath \"%~1\" -PassThru | Get-Volume).Driveletter" 2^>NUL') Do Set "ISODrv=%%G"
If Not Defined ISODrv (Exit /B 3) Else Echo %ISODrv%
Doing it this way eliminates the need for pre-creating a PowerShell script, and then any subsequent modifications to the execution policy. It only proceeds with the image mount if the received input value was an existing ISO file too. If you're running this batch file from a process which retrieves its exit code, 1 means that the input did not end with the case insensitive string .iso, 2 would mean that the input did end with the case insensitive string .iso, but it was a directory, not a file, and 3 would indicate that there was an error returning the mounted ISO image drive letter.
Try the following to run the cmd from the PowerShell and pathing their variables to it
# The command to pass to cmd.exe /cript
$var = "echo hello world & ping $ip & pause"
$ip = "192.168.1.1"
$var2 = "ping $ip & pause"
# Start the process asynchronously, in a new window,
# as the current user with elevation (administrative rights).
# Note the need to pass the arguments to cmd.exe as an *array*.
Start-Process -Verb RunAs cmd.exe -Args '/c', $var2, $var

How to pass batch file variables to PowerShell script? [duplicate]

This question already has an answer here:
Displaying SET variable
(1 answer)
Closed 3 years ago.
I am attempting to pass a number of variables processed by my batch file to a Powershell script. The problem I face is that firstly the entire results from the batch file come up in command prompt and next to the variables I intend to pass are not passed to the Powershell Script. Additionally, the variable I have to output the contents of the log file in just send the command back to the screen.
I have tried the following links and these links got me as far as I am now:
Batch file to execute a Powershell script
Pass variable from batch to powershell
Pass parameter from a batch file to a PowerShell script
Pass batch variables with spaces in them to powershell script?
Batch File side
set LOG_FILE = "GDGAGnklasj;oks;fk;dkf lkl;"
set oName = Name
set oStart = "%YYYY%%MM%%DD% %TIME%"
set oStatus = 0
set oEnd = "%YYYY%%MM%%DD% %TIME%"
set oDateRan = %YYYY%%MM%%DD%
set oLog =for /f "delims=" %%i in (%LOG_FILE%) do set content=%content% %%i
echo Updating Database >> %LOG_FILE% 2>&1
cmd /S powershell.exe -ExecutionPolicy Bypass -File "C:\Reporting\updateTool.ps1" "%oName%" "%DateRan%" "%oStart%" "%oEnd%" "%oStatus "%oLog%
PowerShell Script
param (
[string]$oName
)
"This is $oName"
My intent is to set the variables within the batch file then send them to Powershell for processing.
Be very careful of spaces.
set oName=taco
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '.\ScriptName.ps1' -oName '%oName%' "
Oh Easy-Peasy, I do this for my Power shells that we need CMD wrappers for quite a bit.
I have to run to the train so this is going to be a bit meh at the moment I will firm it up in a bit, right now just going to paste in some example code so I can make it your code
Okay, what, umm, what did you intend for this Particular code to.l do ? I can't seem to figure out what you were intending with this, is it just some dummy code?
set oLog =for /f "delims=" %%i in (%LOG_FILE%) do set content=%content% %%i
echo Updating Database >> %LOG_FILE% 2>&1
Okay on further review I think you want to read the log into a couple of sttring variables in CMD, then use one of them in your call of the script..... but, why?
The strings will append to each other and you will be limited to 8191 characters max, and PowerShell can easily read the content of the log file because you pass the name to Powershell.
That seems like a better plan, no?
All your code where you have YYYY MM DD those are variables you will need to define before using, not sure if that is understood if so all good.
.CMD Script:
#(
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO OFF
SET "_PSScript=C:\Reporting\UpdateTool.ps1"
REM SET "_DebugPreference=Continue"
SET "_DebugPreference="SilentlyContinue"
SET "_LOG_FILE=GDGAGnklasj;oks;fk;dkf lkl;"
SET "_oName=Name."
SET "_oStart=%YYYY%%MM%%DD% %TIME: =0%"
SET /a "_Status=0"
SET "_oEnd=%YYYY%%MM%%DD% %TIME: =0%"
SET "_oDateRan=%YYYY%%MM%%DD%"
)
SET "_PSCMD=Powershell "%_PSScript%" -DebugPreference "%_DebugPreference%" -LOG_FILE "%_LOG_FILE%" -oName "%_oName%" -oStart "%_oStart%" -Status %_Status% -oEnd "%_oEnd%" -oDateRan "%_oDateRan%" "
%_PSCMD% 2>&1 >> "_LOG_FILE"
PS1:
## Script: UpdateTool.ps1
#
param(
[String]$LOG_FILE = 'c:\admin\default.log',
[String]$oName = 'default name'
[String]$oStart = $(Get-date -F "yyyyMMdd HH:mm:ss.ms"),
[Int]$oStatus = 0,
[String]$oEnd = $(Get-date -F "yyyyMMdd HH:mm:ss.ms"),
[String]$oDateRan = $(Get-date -F "yyyyMMdd"),
$DebugPreference = "SilentlyContinue"
)

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.