Powershell - get console output into variable (not stdout, not stderror) - powershell

I have the exe from Lenovo that only provides command line output when run from a cmd window but not from PowerShell. The output seems to come from a ghost source other than StdOut or StdErr.
https://download.lenovo.com/pccbbs/mobiles/n2hgc06w.exe (you have to run and click install but all that does is unzip to c:\drivers\win\touchpadfw). Be sure to cancel the install prompt after extract.
This command gives me output to the console
& cmd.exe /c c:\drivers\win\touchpadfw\touchpadfw_aug2019\synreflash.exe /v /S 2
This also gives me output the the console and nothing in the variable
$var = (& cmd.exe /c c:\drivers\win\touchpadfw\touchpadfw_aug2019\synreflash.exe /v /S 2) 2>&1
Same here
$var = (& cmd.exe /c c:\drivers\win\touchpadfw\touchpadfw_aug2019\synreflash.exe /v /S 2 2>&1)
I feel like this exe is outputting in some other way than StdOut and StdErr but I don't know what. Nothing I've tried can capture what it is outputting. Is there a third method of output?
This is where it gets weird. Using "start cmd" to open a cmd window from the admin PowerShell, I run the exe directly in the cmd window but the output goes to the parent powershell console. I cannot get any output if I start the cmd window as admin directly.

Look at the SynReflash usage below you will notice that you would need to pass the last arg as /S 3 explicitly, to print to Standard Output as opposed to /S 2 is a silent mode
$cmdOutput = cmd.exe /c "C:\DRIVERS\WIN\TouchpadFW\n2hgc06w\synreflash.exe" /v /S 3 '2>&1'
OR
proc = [System.Diagnostics.Process]::Start([System.Diagnostics.ProcessStartInfo]#
{
'FileName' = "cmd.exe"
'Arguments' = "/C " + """C:\DRIVERS\WIN\TouchpadFW\n2hgc06w\synreflash.exe"" /v /s 3"
'CreateNoWindow' = $true
'UseShellExecute' = $false
'RedirectStandardOutput' = $true # to get stdout to $proc.StandardOutput
'RedirectStandardError' = $true # to get stderr to $proc.StandardError
})
$output = $proc.StandardOutput
$error1 = $proc.StandardError
write-host $output.ReadToEnd()
Output
FW Version: 1.2

Related

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

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

Trying to Remotely use PsTools (PsExec) to Return a Result on Powershell

I am trying to run a script remotely that will double check that the IP address is correct using PsExec in Powershell. The problem is I only want it to return the result True or False and not show any other lines within Powershell.
I have tried running background jobs as well but have not seemed to get that working, as when I do that it simply gives me nothing.
function remoteIPTest($Computer) {
$result = & cmd /c PsExec64.exe \\$Computer -s cmd /c "ipconfig"
if ($result -like "*10.218.5.202*") {
return "True"
}
}
$Computer = "MUC-1800035974"
remoteIPTest $Computer
After running this, I just want the application to give return:
True
Instead of returning:
Starting cmd on MUC-1800035974... MUC-1800035974...
cmd exited on MUC-1800035974 with error code 0.
True
psexec prints its status messages to stderr, which a variable assignment such as $result = does not capture, so these messages still print to the screen.
Variable assignments only capture stdout output from external programs such as psexec, which in this case is ipconfig's output.
Therefore, the answer is to suppress stderr, which you can do with 2>$null (2 is the number of PowerShell's error stream, which stderr maps to) - see Redirecting Error/Output to NULL.
Do note that this will also suppress true error messages.
In addition, the cmd /c calls are not needed, because you can use psexec to invoke other programs directly, if you have the path configured properly.
Instead of this:
$result = & cmd /c PsExec64.exe \\$Computer -s cmd /c "ipconfig"
Do This:
$result = PsExec64.exe \\$Computer -s ipconfig 2>$null
Hope it helps.

Powershell: running JScript scripts without logo

I set cscript.exe as my default scripting host with the nologo option. Therefore in cmd.exe
I get, as expected:
> ftype jsfile
jsfile="C:\Windows\System32\CScript.exe" //nologo "%1" %*
> reg query HKCR\jsfile\Shell\Open\Command
HKEY_CLASSES_ROOT\jsfile\Shell\Open\Command
(Default) REG_EXPAND_SZ "C:\Windows\System32\CScript.exe" //nologo "%1" %*
> echo WScript.Echo("Test Echo"); > test.js
> test.js
Test Echo
But, moving to Powershell:
> powershell -nologo
PS > .\test.js
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.
Test Echo
It seems that CScript does not get //nologo from Powershell shell.
How can I fix this?
More use cases
It also works with:
PS > cscript.exe //nologo test.js
Test Echo
PS > cmd /c test.js
Test Echo
PS > cmd /c test
Test Echo
Copy & Paste Test
Someone suggests it can be a bug. To check if it applies to you, you can copy the following in a PS console with elevated privileges:
## Change the host to cscript nologo and store old setting
$jstype="Registry::HKEY_CLASSES_ROOT\jsfile\Shell\Open\Command"
$jsvalue=("`"$env:SystemRoot\System32\CScript.exe`"" + ' //nologo "%1" %*')
$old=(gp -Path $jstype )."(default)"
Set-Item -Path $jstype -value $jsvalue
## The output should be the same (i.e. nologo!)
echo 'WScript.Echo("Test Echo");' > test.js
cmd /c test
.\test.js
## Restore old settings
Set-Item -Path $jstype -value $old
Is cmd /c test and .\test.js output the same?
Details on the problem
It seems that Powershell has a weird behaviour with respect to file associations.
If you paste the following snippet in an elevated privilege PS shell:
$jstype="Registry::HKEY_CLASSES_ROOT\jsfile\Shell\Open\Command"
Set-Item -Path $jstype -value "cscript.exe total non sense"
echo 'WScript.Echo("Test Echo");' > test.js
cmd /c test.js
... you get the deserved error for associating jsfiles with the command string cscript.exe total non sense.
But surprisingly writing:
.\test.js
works like a charm. So it seems that PS only reads the first item of the open-command-string (here cscript.exe) and then appends to it the user string (.\test.js), totally disregarding the other elements of the open-string, here total non sense.
To confirm paste this:
Set-Item -Path $jstype -value "notepad.exe total non sense"
echo 'WScript.Echo("Test Echo");' > test.js
cmd /c test.js
cmd.exe opens the Notepad which proposes to create the file "total non sense.txt".
Now write:
.\test.js
Based on what I just said, the actual command run will be notepad.exe .\test.js and in fact the Notepad will now open .\test.js.
Solution
Whether or not this is a bug, a possible solution is to create a batch script calling the cscript.exe with the desired parameters. I will name it C:\Windows\System32\cscript.cmd.
To create the script and the related association, paste:
echo "#cscript.exe //nologo %*" | out-file -encoding ASCII C:\Windows\System32\cscript.cmd
$jstype="Registry::HKEY_CLASSES_ROOT\jsfile\Shell\Open\Command"
$jsvalue=("`"$env:SystemRoot\System32\cscript.cmd`"" + ' "%1" %*')
Set-Item -Path $jstype -value $jsvalue
As you see //nologo is moved from the registry to cscript.cmd.
Note that echo "string" > would produce a Unicode file, which cmd.exe dislikes, hence the ASCII filter.
Print your new .js opening command:
cmd /c ftype jsfile
That is: jsfile="C:\Windows\System32\cscript.cmd" "%1" %*.
Now both:
cmd /c test
.\test
will produce:
Test Echo
without the banner, and note that in both cases you don't need to specify the .js extension.
For simplicity I set cscript.cmd to run cscript.exe straight. You might want to specify its full path.

Powershell v2 - Installing Printer

I'm trying to automate printer installation on windows 7 x64, by using Powershell script. So far I have a script that successfully creates TCP/IP port but gives me an error - The arguments are invalid, when it executes printer installation part of the code. Any ideas on how to fix the problem and successfully install a printer through the Powershell? The code is as follows:
$hostAddress = "172.16.2.24"
$portNumber = "9100"
$computer = $env:COMPUTERNAME
$wmi= [wmiclass]"\\$computer\root\cimv2:win32_tcpipPrinterPort"
#$wmi.psbase.scope.options.enablePrivileges = $true
$newPort = $wmi.createInstance()
$newPort.hostAddress = $hostAddress
$newPort.name = "IP_" + $hostAddress
$newPort.portNumber = $portNumber
$newPort.SNMPEnabled = $false
$newPort.Protocol = 1
$newPort.put()
CMD.EXE /C "printui.exe /if /b "Test Printer" /f C:\inetpub\wwwroot\ftp\Prdrivers\HP Universal Print Driver\pcl6-x64-5.7.0.16448\hpbuio100l.inf /r "IP_172.16.2.24" /m "HP Laser Jet P3015""
Question Update: This is the working CMD code, so how do I incorporate it into the Powershell code above ?
printui.exe /if /b "HP Universal Printing PCL 6" /f "C:\inetpub\wwwroot\ftp\Prdrivers\HP Universal Print Driver\pcl6-x64-5.7.0.16448\hpbuio100l.inf" /u /r "IP_172.16.2.24" /m "HP Universal Printing PCL 6"
To embed double-quotes within a double-quoted string you need to escape them. Since you are not using variables, it is easier to use a single quoted string e.g.:
CMD.EXE /C 'printui.exe /if /b "Test Printer" /f C:\inetpub\wwwroot\ftp\Prdrivers\HP Universal Print Driver\pcl6-x64-5.7.0.16448\hpbuio100l.inf /r "IP_172.16.2.24" /m "HP Laser Jet P3015"'
If you ever need to use PowerShell variables inside this string, then you will need to switch back to double quotes and escape the necessary DQ characters e.g:
CMD.EXE /C "printui.exe /if /b `"$PrinterName`" /f C:\inetpub\wwwroot\ftp\Prdrivers\HP Universal Print Driver\pcl6-x64-5.7.0.16448\hpbuio100l.inf /r `"IP_172.16.2.24`" /m `"HP Laser Jet P3015`""
Sorry, but im not sure why you are calling CMD /C #PARAMS. I am just calling the printui.exe directly and it is working, and I only double quote the Args
# Printer Info, I keep this in an SQL DB, and return these values with a query:
$printerID = "<PrinterNameOrID>"
$printerIP = "<PrinterIP>"
$printerPort = "IP_$printerIP"
$printerModel = "<PrinterModelFromINF>"
$driverINFPath = "<UNCPathToDriverINF>"
# Build a new Local TCP Printer Port, naming it with values unique to the Printer ID:
$newPort = ([wmiclass]"Win32_TcpIpPrinterPort").CreateInstance()
$newPort.HostAddress = $printerIP
$newPort.Name = $printerPort
$newPort.PortNumber = "9100"
$newPort.Protocol = 1
$newPort.Put()
# Add the printer
printui.exe /if /b "$printerID" /f "$driverINFPath" /u /r "$printerPort" /m "$printerModel"
I know this has already been answered but you could borrow the code I have in this Excel Workbook (there is a link in the article). I realize it uses VBS but these are built in scripts in Windows and cutting / pasting into Excel has saved me many times and I've installed thousands of printers this way
best-tool-for-printer-creation-excel-vs-print-management-console
Try this:
runas /user:Contoso.com\user1 "printui.exe /if /b \"Test Printer\" /f \"C:\inetpub\wwwroot\ftp\Prdrivers\HP Universal Print Driver\pcl6-x64-5.7.0.16448\hpbuio100l.inf\" /r \"IP_172.16.2.24\" /m \"HP Laser Jet P3015\""