VBScript, Batch or PowerShell Script? - powershell

I'm trying to run various commands using psexec.exe from Windows Sysinternals. What I need is a simple script to read the output of those commands.
For example if everything went OK, then it returns a 0. If something went wrong, then it will spit out an error code.
How can it be done?

In PowerShell, you would use the $LastExitCode variable to test if psexec succeeded or not e.g.:
$results = psexec <some command on remote system>
if ($LastExitCode -ne 0) {
throw "PSExec failed with error code $LastExitCode"
}
return 0

In a batch file, you use the %ERRORLEVEL% variable, or the IF ERRORLEVEL n command. For example:
psexec \\host -i findstr.exe "test" c:\testfile
if errorlevel 1 (
echo A problem occurred
)
IF ERRORLEVEL checks whether the return value is the same or higher than the number you specify.
This is not the same as capturing the output of the command though. If you actually want the output, you need to include redirection to an output file on the command line:
psexec \\host -i cmd.exe /c findstr "test" c:\testfile ^> c:\output.txt
The ^ is necessary to escape the > character, or the redirection would happen locally instead of on the remote machine. The cmd.exe is necessary, because redirection is handled by cmd.

Related

How do I get the ErrorLevel from a process started via PowerShell Start-Process called from CMD?

I'd like to be able to get the ErrorLevel of Process.exe from this example command:
C:\>PowerShell -NoP -C "Start-Process "C:\Process.exe""
If Process.exe exits with an ErrorLevel of 5, I'd like to be able to capture that via CMD, ideally in the %ErrorLevel% variable. I'm not proficient enough in PowerShell to do this.
Use the following, assuming that C:\process.exe is a console application:[1]
PowerShell -NoP -C "C:\Process.exe; exit $LASTEXITCODE"
Note: If the executable path contains spaces, enclose it in '...'; if it contains ' itself, either escape the enclosed ' chars. as '', or enclose the path in \"...\" (sic) instead - see this answer for more information.
In order to get a process' exit code, you must wait for it to terminate.
Start-Process does not wait for termination by default, unless you add the -Wait switch - but then you'd also need -PassThru in order to return a process-information object whose .ExitCode property you'd need to report via exit.
Direct execution of the target executable, as shown above, (a) results in synchronous execution of console applications[1] to be begin with and (b) automatically reflects the process exit code in the automatic $LASTEXITCODE variable variable.
Without the exit $LASTEXITCODE statement, the process' exit code would be mapped to an abstracted exit code: 0 (success) would be reported as-is, but any nonzero exit code would be mapped to 1 - see this post for more information.
Either way, PowerShell's (powershell.exe's) exit code will be reflected in cmd.exe's dynamic %ErrorLevel% variable.
[1] If your application happens to be a GUI-subsystem application, you'll indeed need a Start-Process-based solution; as you've noted in a comment yourself, the solution would be:
PowerShell -NoP -C "exit (Start-Process 'C:\Process.exe' -Wait -PassThru).ExitCode"

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 via batch file multiple statements

This code is very close to being finished, however I cannot get the last statement (psexec \\%PC% -i -d -s "\.exe) that I wish to run, to run only if the device pings.
The aim is, if a device pings then write to host that it pings and run the psexec command else write to host that it hasn't been able to ping.
Code:
#ECHO OFF
cls
Clear-Host
set /p PC=PC no?:
FOR %%i IN (
%PC%
) DO (
PowerShell -NoProfile -Command "If (Test-Connection %%i -Count 1 -Quiet) { Write-Host "%%i - successfully pinged" -F Green } (psexec \\%PC% -i -d -s "\.exe that i wish to run")
else { Write-Host "%%i FAILED" -F Red}"
)
pause
This is like Spanglish or something. Clear-Host isn't a command that cmd understands, and it'd be easier just to ping.
#echo off
setlocal
if "%~1"=="" (
set /P "PC=PC no? "
) else (
set "PC=%~1"
)
ping -n 1 %PC% | find /i "TTL=" >NUL 2>NUL && (
powershell "Write-Host '%PC% passed' -f Green"
psexec \\%PC% -i -d -s "\.exe that i wish to run"
) || (
powershell "Write-Host '%PC% failed' -f Red"
)
For what it's worth, if you don't need the output of the command that psexec runs, I generally find wmic process call create to be faster than psexec.
wmic /node:%PC% process call create "exe that you wish to run"
If you need to supply auth info, add the /user:username and /password:password switches before process call create.
There are several problems with your code above. When executing a PowerShell command within a batch script, you can't break the line very easily. You should also include your psexec command within the if code block, rather than after it as you have now. You can separate commands with your braced code block with semicolons if needed. And finally, you need to be careful mixing double quotes as part of the powershell command token with the double quotes within the command. It could be fixed so that powershell handles the conditional flow, but my solution above works just as well and is more readable I think.

How can I detect whether or not I am in powershell from a command line?

I am creating a standard windows BAT/CMD file and I want to make an IF statement to check whether or not this CMD file is run from PowerShell. How can I do that?
Edit: My underlying problem was that test.cmd "A=B" results in %1==A=B when run from CMD but %1==A and %2==B when run from PowerShell. The script itself is actually run as an old Windows Command line script in both cases, so checking for Get-ChildItem will always yield an error.
One way, it to see what your process name is, and then check its attributes:
title=test
tasklist /v /fo csv | findstr /i "test"
As long as you use a unique name in place of Test, there should be little room for error.
You will get back something like:
"cmd.exe","15144","Console","1","3,284
K","Running","GNCID6101\Athomsfere","0:00:00","test"
When I ran the above code from a bat file, my output was:
"powershell.exe","7396","Console","1","50,972
K","Running","GNCID6101\Athomsfere","0:00:00","
A potentially simpler approach that may work for you. If not, it may be suitable for others.
Create 2 separate script files: test.ps1 and test.cmd
Don't include extension when calling the script. I.e. call as <path>\test (or just test if folder is in the path environment variable.
This works because CMD prioritises which script to execute as: .bat > .cmd, whereas Powershell prioritises: .ps1 > .bat > .cmd.
The following is the output of a CMD session:
C:\Temp>copy con test.cmd
#echo cmd^Z
1 file(s) copied.
C:\Temp>copy con test.ps1
Write-Output "ps1"^Z
1 file(s) copied.
C:\Temp>.\test
cmd
C:\Temp>
And calling test from Powershell:
PS C:\Temp> .\test
ps1
PS C:\Temp>
Couldn't you try to execute a Get-ChildItem and then check %ERRORLEVEL% to see if it returns an exe not found?
http://ss64.com/nt/errorlevel.html

Send "any key" to cmd.exe from powershell script

I have following PowerShell script:
cmd /c script1.bat
cmd /c script2.bat
script1.bat at the end of execution have "pause" command, so the execution of my PS script stops.
How can send any key cmd.exe to avoid stopping script execution?
NOTE: I can't change batch scripts - they are 3rd party.
You can pipe input to the program (cmd.exe) like this:
"X" | cmd /c script1.bat
You could put an empty file called pause.bat in the same directory. Then it would do nothing instead of pause.
The batch scripts may be 3rd party, but surely you can just a copy/backup and edit the content to remove the PAUSE command?
I sometimes put a PAUSE in if I am testing something and don't want the window to close, but otherwise I can't think of a good reason to keep that in.
I had trouble getting the accepted answer to work for me due having an expression in the bat file path.
"x" | $Env:WRAPPER_HOME\bat\installService.bat $LOGFILE
Error is "Expressions are only allowed as the first element of a pipeline."
Here's what I got working (finally):
[PS script code]
& runner.bat bat_to_run.bat logfile.txt
[runner.bat]
#echo OFF
REM This script can be executed from within a powershell script so that the bat file
REM passed as %1 will not cause execution to halt if PAUSE is encountered.
REM If {logfile} is included, bat file output will be appended to logfile.
REM
REM Usage:
REM runner.bat [path of bat script to execute] {logfile}
if not [%2] == [] GOTO APPEND_OUTPUT
#echo | call %1
GOTO EXIT
:APPEND_OUTPUT
#echo | call %1 1> %2 2>&1
:EXIT