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

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

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

Powershell command run on CMD with if condition

Example 1:
for /F "tokens=3 delims= " %%A in ('manage-bde -status %systemdrive% ^| findstr " Encryption Method:"') do (
if "%%A"=="AES" goto EncryptionCompleted
)
:EncryptionCompleted
Example 2:
for /F %%A in ('wmic /namespace:\\root\cimv2\security\microsofttpm path win32_tpm get IsEnabled_InitialValue ^| findstr "TRUE"') do (
if "%%A"=="TRUE" goto nextcheck
)
:nextcheck
Please help to find the below code as run on .bat to stop script execution.
The command is:
powershell.exe (Get-Tpm | Select -Property TpmReady).TpmReady -eq $False
then goto Failed
:Failed
Since you're only looking to act on a Boolean value, you can communicate that via the PowerShell process' exit code, with 0 corresponding to $true and 1 to $false, given that the widely observed convention is that exit code 0 signals success, whereas any nonzero exit code signals an error condition.
Boolean values in PowerShell can directly be converted to integers, which, however, performs the opposite mapping: [int] $true is 1 and [int] $false is 0.
Therefore, the logic must be reversed with -not before passing the Boolean to PowerShell's exit statement.
On the cmd.exe (batch-file) side, this allows you to act on the exit code with the || operator, which only executes the RHS in case of failure, i.e. if the LHS command reported a nonzero exit code (such as 1).
powershell.exe -noprofile -c "exit -not (Get-Tpm).TpmReady" || goto :FAILED
echo "TPM is ready."
exit /b 0
:FAILED
echo "TPM is NOT ready." >&2
exit /b 1
Note that I've added the following CLI parameters to the PowerShell call: -noprofile to potentially speed up execution, and -c (-Command) to explicitly signal that a command (piece of PowerShell code) is being passed.
A demo of doing everything in powershell.
(get-bitlockervolume $env:systemdrive).encryptionmethod
None
(get-ciminstance -namespace root\cimv2\security\microsofttpm win32_tpm).
IsEnabled_InitialValue
True
(get-tpm).TpmReady
True

Multiple inputs into new prompt & Powershell -run as and -nonewwindow issue

Here is what I currently do, file 1:
powershell.exe -command "Start-Process cmd -ArgumentList '/c cd C:\ && DiskZero.cmd'-Verb runas"
And file 2 "DiskZero.cmd":
#echo off
(echo rescan
echo sel disk 1
echo cle all
echo cre part prim
echo for fs=ntfs quick label=Intenso
echo assign letter=E
) | diskpart
pause
It works as intended, however, there is two files, what I want to do is make it so there's only one file.
I can't manage to find how to input multiple lines of code into a new elevated command prompt with only one script, so instead I'm trying to do it with powershell:
start cmd -nonewwindow works
start cmd -ver runas works
however start cmd -nonewwindow -ver runas doesn't work
What I was hoping to do was this in powershell:
start cmd -nonewwindow -ver runas
#echo off
(echo rescan
echo sel disk 1
echo cle all
echo cre part prim
echo for fs=ntfs quick label=Intenso
echo assign letter=E
) | diskpart
pause
Can anyone help me solve the start cmd -nonewwindow -ver runas issue OR input multiple lines of code into a new elevated command prompt with only one file, please?
Can anyone help me solve the start cmd -nonewwindow -verb runas issue
Unfortunately, there is no solution: Windows fundamentally does not allow you to run an elevated process (run as admin, requested with -Verb RunAs) directly in a non-elevated process' console window - that is why Start-Process syntactically prevents combining -NoNewWindow with -Verb RunAs.
OR input multiple lines of code into a new elevated command prompt with only one file, please?
While there is a solution, it'll be hard to maintain:
You can pass the lines of your second batch file (the one you want to eliminate) to cmd /c on a single line, joined with &:
Note: To facilitate side effect-free experimentation, the original diskpart command was replaced with findstr -n ., which merely prints the lines received via stdin, preceded by their line number.
powershell.exe -command "Start-Process -Verb RunAs cmd '/c cd C:\ && (echo rescan&echo sel disk 1&echo cle all&echo cre part prim&echo for fs=ntfs quick label=Intenso&echo assign letter=E) | findstr -n .&pause'"
That no space char. precedes each & is deliberate, because trailing whitespace in echo commands is significant, i.e. it becomes part of the output; however, it should be fine to place a space char. after each & (as well as before, if the preceding command ignores trailing whitespace).
A better solution is to create a temporary helper batch file from your batch file, pass its path to the PowerShell command, and delete it afterwards:
#echo off
:: Determine the path for a temporary batch file...
:: Note: %~snx0 uses the short (8.3) name of the batch file, so as
:: to ensure that the temp. file path has no spaces, which
:: obviates the need for complex double-quoting later.
set "tmpBatchFile=%TEMP%\~%~snx0"
:: ... and fill it with the desired commands.
:: Note how metacharacters - ( ) | ... - must be ^-escaped.
(
echo #echo off
echo ^(echo rescan
echo echo sel disk 1
echo echo cle all
echo echo cre part prim
echo echo for fs=ntfs quick label=Intenso
echo echo assign letter=E
echo ^) ^| findstr -n .
echo pause
) > "%tmpBatchFile%"
:: Now you can let the elevated cmd.exe process that PowerShell launches
:: execute the temp. batch file.
:: Note: -Wait ensures that the PowerShell call blocks until the elevated
:: cmd.exe window closes.
powershell.exe -command "Start-Process -Wait -Verb RunAs cmd '/c cd C:\ & %tmpBatchFile%'"
:: Delete the temp. batch file.
:: Note: If you do NOT use -Wait above, you'll have to defer deleting
:: the batch file until after the elevated cmd.exe window closes,
:: which you'll have to do manually.
del "%tmpBatchFile%"

Windows / Powershell get Program Version into variable

I'm close but not there. I can get the version of my application via powershell, but it's got a bunch of text along with it.
This command:
powershell -NoLogo -NoProfile -Command ^
(get-item -Path 'c:\myapp.exe').VersionInfo ^| ^
Format-List -Force | findstr "ProductVersion" > c:\version.txt
produces (in a text file):
ProductVersion : 1.6.7.0
Is it possible via a single command in powershell to split it? I can't run ps scripts in my environment. But if I could, I would run this:
$mystr = (get-item -Path 'c:\myapp.exe').VersionInfo | Format-List -Force | findstr ProductVersion
$arr = $mystr -split ": "
$arr[1]
Is there a way to put this on a single line and put it into a environment (batch) variable?
Given your provided method, with some modification, perhaps this would do it?
#Echo Off
For /F "Delims=" %%A In ('Powershell -C^
"(GI 'C:\myapp.exe').VersionInfo.ProductVersion"') Do Set "PV=%%A"
Echo=%PV%
Pause
Mayhap
| for /f "tokens=3" %%a in ('findstr "ProductVersion"') do echo %%a>filename
or
| for /f "tokens=3" %%a in ('findstr "ProductVersion"') do set "prodver=%%a"
or
| for /f "tokens=3" %%a in ('findstr "ProductVersion"') do setx prodver "%%a"
but no guarantees. Note the setx version may establish a registry entry for future process instances, not for the current instance. /m would need to be added to make it a HKLM instead of a HKCU variable (if it works)
You can also use WMIC to get version of your application :
#echo off
Title Get File Version of any Application using WMIC
Set "Version="
Set "AppFullPath=%Windir%\notepad.exe"
Call :Get_AppName "%AppFullPath%" AppName
Call :Add_backSlash "%AppFullPath%"
Call :GetVersion %Application% Version
If defined Version (
echo Vesrion of %AppName% ==^> %Version%
)
pause>nul & Exit
::*******************************************************************
:Get_AppName <FullPath> <AppName>
Rem %1 = FullPath
Rem %2 = AppName
for %%i in (%1) do set "%2=%%~nxi"
exit /b
::*******************************************************************
:Add_backSlash <String>
Rem Subroutine to replace the simple "\" by a double "\\" into a String
Set "Application=%1"
Set "String=\"
Set "NewString=\\"
Call Set "Application=%%Application:%String%=%NewString%%%"
Exit /b
::*******************************************************************
:GetVersion <ApplicationPath> <Version>
Rem The argument %~1 represent the full path of the application
Rem without the double quotes
Rem The argument %2 represent the variable to be set (in our case %2=Version)
FOR /F "tokens=2 delims==" %%I IN (
'wmic datafile where "name='%~1'" get version /format:Textvaluelist 2^>^nul'
) DO FOR /F "delims=" %%A IN ("%%I") DO SET "%2=%%A"
Exit /b
::*******************************************************************
Just use the ProductVersion property on the VersionInfo object and assign the result to an environment variable:
$ENV:MyEnvVariable = (get-item -Path 'c:\myapp.exe').VersionInfo.ProductVersion

how do i search for a file and when found set the file path to a variable in a batch script

I am trying to create a batch file that will run and open a Powershell script to then run.
this is what i have so far
#echo off
for /r C:\folder %%a in (*) do if "%%~nxa"=="2WNRN4VMS2.txt" set p=%%~dpnxa
if defined p (
echo %p%
) else (
echo File not found
Pause
)
Powershell.exe -Command "& '%p%'"
exit
That is very simple using command DIR for searching for the file recursively in folder C:\folder and all its subfolders and command FOR for assigning the drive and path of found file to an environment variable:
#echo off
for /F "delims=" %%I in ('dir /A-D /B /S "C:\folder\2WNRN4VMS2.txt" 2^>nul') do set "FilePath=%%~dpI" & goto FoundFile
echo File not found
pause
goto :EOF
:FoundFile
Powershell.exe -Command "& '%FilePath%'"
Please note that the string assigned to environment variable FilePath ends with a backslash. Use in PowerShell command line %FilePath:~0,-1% if the path of the file should be passed without a backslash at end.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
dir /?
echo /?
for /?
goto /?
pause /?
set /?