Why if $LastExitCode=0 my SCHTASKS at CMD returns $?=False - powershell-4.0

This code works perfectly in returning scheduled tasks I'm looking for - but why does it send a False value for $?
SCHTASKS /Query /FO csv /v /s $Computer /U myuser /P 'mypass' | ConvertFrom-Csv | where {$_.TaskName -Like "*mytext*"}| Select TaskName , "Last Run Time", Author
$?
$LASTEXITCODE

Related

Get only PID from tasklist using cmd title

Desired output:
1234
Just the PID. Nothing else - no other characters, numbers, or symbols.
I'm trying to run tasklist so it gives me only the PID of a named or titled process.
tasklist | findstr /i "cmd.exe" is the closest I've gotten, but the result is too verbose. I just want the PID number.
Bonus points for linking me a description of what the tasklist filter operators mean - "eq", "ne", etc, since they aren't anywhere in the documentation.
The difficult thing with tasklist is its default output format. For example, when command line:
tasklist /FI "ImageName eq cmd.exe" /FI "Status eq Running"
is executed, we get:
Image Name PID Session Name Session# Mem Usage
========================= ======== ================ =========== ============
cmd.exe 12740 Console 1 3'328 K
cmd.exe 11020 Console 1 3'304 K
Unless the column widths are fixed, which I would not rely on, extracting the PID is not that trivial, because the image name could also have SPACEs in it, so using such as delimiters would not work.
A possible way was to count the number of =-signs in the second line up to the first SPACE, so we know the number of characters to truncate to have the image name removed, but this requires some kind of loop (using goto), so the performance might be quite bad.
However, there are other output formats available for tasklist. The command line:
tasklist /FI "ImageName eq cmd.exe" /FI "Status eq Running" /FO CSV
results in this output:
"Image Name","PID","Session Name","Session#","Mem Usage"
"cmd.exe","12740","Console","1","3'328 K"
"cmd.exe","11020","Console","1","3'304 K"
Now it is quite easy to extract the PID:
#echo off
for /F "delims=" %%R in ('
tasklist /FI "ImageName eq cmd.exe" /FI "Status eq Running" /FO CSV /NH
') do (
set "FLAG1=" & set "FLAG2="
for %%C in (%%R) do (
if defined FLAG1 (
if not defined FLAG2 (
echo %%~C
)
set "FLAG2=#"
)
set "FLAG1=#"
)
)
Another output formats is used by the following command line:
tasklist /FI "ImageName eq cmd.exe" /FI "Status eq Running" /FO LIST
resulting in this output:
Image Name: cmd.exe
PID: 12740
Session Name: Console
Session#: 1
Mem Usage: 3'328 K
Image Name: cmd.exe
PID: 11020
Session Name: Console
Session#: 1
Mem Usage: 3'304 K
With this it is even simpler to get the desired output:
#echo off
for /F "tokens=2" %%K in ('
tasklist /FI "ImageName eq cmd.exe" /FI "Status eq Running" /FO LIST ^| findstr /B "PID:"
') do (
echo %%K
)
By the way, for the filter options /FI, there are the following operators available:
eq -- equal to;
ne -- not equal to;
gt -- greater than;
lt -- less than;
ge -- greater than or equal to;
le -- less than or equal to;
The Microsoft documentation as well as the help message (tasklist /?) do not explain their meaning, but I found the following external resources:
Managing Windows Programs from the Command Line: Tasklist
Microsoft DOS tasklist command
Use for /f to parse output. The PID is the 2nd column space separated.
The default separator/delmiter is the space, adjacent delims count as one.
So this sample ouput:
> tasklist | findstr /i "cmd.exe"
cmd.exe 14924 Console 9 6.008 K
Is parsed on the cmd line
for /f "tokens=2" %A in ('tasklist ^| findstr /i "cmd.exe" 2^>NUL') do #Set "PID=%A"
Or in a batch:
#Echo off & SetLocal EnableDelayedExpansion
set "PID="
for /f "tokens=2" %%A in ('tasklist ^| findstr /i "cmd.exe" 2^>NUL') do #Set "PID=!PID!,%%A"
if defined PID Echo cmd.exe has PID(s) %PID:~1%
cmd.exe has PID(s) 14924,11268,3652
The last one presumably the temporary one used by the for /f itself.
EDIT late addition to my answer.
The tool cmdow from Ritchie Lawrence can accomplish your task.
With this question open in firefox:
> cmdow.exe "get only PID from task*" /f
Handle Lev Pid -Window status- Image Caption
0x0103DE 1 9532 Max Ina Ena Vis firefox Get only PID from tasklist using cmd title - Stack Overflow - Mozilla Firefox
To only get the PID on cmd line
> for /f "tokens=3" %A in ('cmdow.exe "get only PID from task*" /B /F') Do #Echo:%A
9532
In a batch file double the percent signs %%A.
Demonstration using start to run another cmd.exe with a specified title:
:: Q:\Test\2018\05\27\SO_50555929_2.cmd
#Echo off
set "MyTitle=This is a quite long title to distinguish from other"
start "%MyTitle%" cmd.exe /k cmdow.exe # /F
:: wait to get other cmd instance get started
timeout /t 3 >NUL
set "PID="
for /F "tokens=3" %%A in ('cmdow.exe "%MyTitle%*"') do set "PID=%%A"
if defined PID Echo %PID%
I am not sure what you are expecting. There could be several cmd shells running. If they are running a program, their window title typically changes.
It is easy enough to get the cmd processes and there PID (the Id field). The window title is also available.
PS C:\src\t\s1> Get-Process | Where-Object { $_.ProcessName -eq 'cmd' }
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
152 15 10280 16432 10.80 2808 1 cmd
153 13 8472 12220 3.68 7232 1 cmd
PS C:\src\t\s1> Get-Process | Where-Object { $_.ProcessName -eq 'cmd' } | ForEach-Object { $_.MainWindowTitle }
cmd.exe
dirlist.bat (C:\src\t) - VIM
Getting the PID only to appear is easy enough. But, which one do you want?
PS C:\src\t\s1> Get-Process | Where-Object { $_.ProcessName -eq 'cmd' } | ForEach-Object { $_.Id }
2808
7232

Setting Multiple Variables from a String of Text

So, I'm running the command netsh wlan show profiles and I want to filter out all of the output except for the strings that include the SSID names into separate variables. The output looks similar to this:
All User Profile : String1
All User Profile : String2
All User Profile : String3
All User Profile : String4
All User Profile : String5
All User Profile : String6
All User Profile : String7
All User Profile : String8
and so on.
How would I go about getting each string by itself without the All User Profile : behind it, and then setting it into a variable, with each string having its own seperate variable? I would like to keep it in CMD and Powershell. I know there's the CMD for command, but the best I could come up with is
for /f "delims=" %%a in ('
netsh wlan show profiles
^| findstr " All User Profile : "
') do set "code=%%a"
which would only set one, and it also wouldn't filter out the All User Profile : part of the command, it would take the whole line which is not what I want.
I found this page, but I don't think that will work.
EDIT: So I've made some progress, but I don't like it because it's sloppy and uses temporary files.
netsh wlan show profiles | findstr /v "Wi-Fi:" | findstr /v "profiles" | findstr /v "^-" | findstr /v "None" > test.txt
powershell -Command "(gc test.txt) -replace ' All User Profile : ', '' | sc test2.txt"
Also, doing this if I were to set the contents of the text file just made into a variable, it would only be the first line of the file, and would only make one variable, which are both problems.
EDIT 2: Made everything wayyy more specific.
EDIT 3: Ok, I'm so close now, sorry for not being specific enough earlier.
This is what I have right now:
for /f "delims=" %%a in ('
powershell -command "netsh wlan show profiles | Select-String '^ All User Profile : (.*)' | ForEach-Object {$_.Matches[0].Groups[1].Value}"
') do set "code=%%a"
But the problem is that it sets each string as code, so they just overwrite each other.
I'm thinking something like this but I don't know the syntax well enough.
for /f "delims=" %%a in ('
powershell -command "netsh wlan show profiles | Select-String '^ All User Profile : (.*)' | ForEach-Object {$_.Matches[0].Groups[1].Value}"
') do (
set "code1=%%a"
set "code2=%%a"
set "code3=%%a"
set "code4=%%a"
set "code5=%%a"
set "code6=%%a"
set "code7=%%a"
set "code8=%%a"
)
Here's the continuation of this
I did get it, here's the final code:
$array = netsh wlan show profiles |
ForEach-Object {
if ($_ -match "\s*All User Profile\s*:\s*(.*)") { $($matches[1]) }
}
foreach ($wn in $array) {
netsh WLAN show profile name=$wn
}
PowerShell:
command | Select-String '^Output: (.*)' | ForEach-Object {
$_.Matches[0].Groups[1].Value
}
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q45225939.txt"
for /f "tokens=1,2*delims=: " %%a in ('
TYPE "%filename1%"
^| findstr /n "Output: "
') do set "code%%a=%%c"
SET code
GOTO :EOF
I used a file containing your data and typed it to simulate your command.
The findstr will assign a serial number to the detected line because of the /n option, so the output will be eg. 2:>>Output: String1. Tokenise that using : and space; %%a becomes 2, %%b >>Output and %%c String1
set code simply lists all of the variables whose names start code

Redirect Powershell output available to command line

I was wondering how to make available powershell variable in order to be read from the command line. Is that possible?
The command is
get-wmiobject win32_networkadapter -filter "netconnectionstatus = 2" | Select -Expand macaddress -Last 1 | set-variable -name mac1
There isn't Powershell in DOS, so I'd guess you have a CMD script (i.e. a .bat or .cmd file) that needs to assign a variable returned from Powershell. This is suprisingly quite tricky.
C:\>for /f "delims=" %i in ('powershell -command " & { get-wmiobject win32_networkadapter -filter 'netconnectionstatus
= 2' | Select -Expand macaddress -Last 1 } "') do set foobar=%i
C:\>set foobar=00:19:99:E1:98:32
C:\>echo %foobar%
00:19:99:E1:98:32

Script To find installed software

We are planning to write a script which will get all the software installed in the Windows system and compare with the list which we have listed and send the result by mail.
I tried a lot and got the below script. The issue is it wont get all the programs installed in the system. Most of the software are missing and also compare is not working. Please help me to improve my script.
for /f "tokens=*" %%i in (D:\BatchScript\ListeProgs.txt) do echo %%i >>D:\BatchScript\newfile.txt
#echo off > D:\BatchScript\installed-programs.csv
regedit /e D:\BatchScript\regexport.txt "HKEY_LOCAL_MACHINE\Software\MicrosoftWindows\CurrentVersion\Uninstall"
find "DisplayName" < D:\BatchScript\regexport.txt > D:\BatchScript\regprogs.txt
for /f "tokens=enter code here2 delims==" %%a in (D:\BatchScript\regprogs.txt) do (
echo %%~a >>D:\BatchScript\installedprogs.txt )
for /f "tokens=*" %%L in (D:\BatchScript\installedprogs.txt) do (
call :sub1 %%L )
goto :eof
:sub1
>> installed-programs.csv echo %1,%2,%3,%4,%5,%6,%7,%8,%9
::== DONE
This is the script that will solve your problem.
After running this code in Powershell you will get a function named Get-InstalledAppsDifferences:
function Get-InstalledAppsDifferences {
Process {
Get-WmiObject -Class Win32_Product | Select-Object -Property Name | Sort-Object -Property Name -Unique > c:\LatestList.txt
IF (Test-Path C:\PreviousList.txt)
{
Compare-Object -ReferenceObject (Get-Content C:\latestList.txt) -DifferenceObject (Get-Content C:\PreviousList.txt) > "C:\Diff_$(get-date -f yyyy-MM-dd).txt"
Remove-Item C:\PreviousList.txt -Force
}
Move-Item C:\LatestList.txt C:\PreviousList.txt
}
}
To run the function inside powershell:
Get-InstalledAppsDifferences
The function generates a list of currently installed programs in C:\LatestList.txt then looks for a file named C:\PreviousList.txt, if found generates differences and saves into a file name C:\Diff_yyyy-mm-dd.txt and removes the previous list.
At the end it renames LatestList.txt to PreviousList.txt for the next use.

How to check if a service is running via batch file and start it, if it is not running?

I want to write a batch file that performs the following operations:
Check if a service is running
If is it running, quit the batch
If it is not running, start the service
The code samples I googled so far turned out not to be working, so I decided not to post them.
Starting a service is done by:
net start "SERVICENAME"
How can I check if a service is running, and how to make an if statement in a batchfile?
I'm a bit confused. What is the argument I have to pass onto the net start? The service name or its display name?
To check a service's state, use sc query <SERVICE_NAME>. For if blocks in batch files, check the documentation.
The following code will check the status of the service MyServiceName and start it if it is not running (the if block will be executed if the service is not running):
for /F "tokens=3 delims=: " %%H in ('sc query "MyServiceName" ^| findstr " STATE"') do (
if /I "%%H" NEQ "RUNNING" (
REM Put your code you want to execute here
REM For example, the following line
net start "MyServiceName"
)
)
Explanation of what it does:
Queries the properties of the service.
Looks for the line containing the text "STATE"
Tokenizes that line, and pulls out the 3rd token, which is the one containing the state of the service.
Tests the resulting state against the string "RUNNING"
As for your second question, the argument you will want to pass to net start is the service name, not the display name.
To toggle a service use the following;
NET START "Distributed Transaction
Coordinator" ||NET STOP "Distributed
Transaction Coordinator"
You can use the following command to see if a service is running or not:
sc query [ServiceName] | findstr /i "STATE"
When I run it for my NOD32 Antivirus, I get:
STATE : 4 RUNNING
If it was stopped, I would get:
STATE : 1 STOPPED
You can use this in a variable to then determine whether you use NET START or not.
The service name should be the service name, not the display name.
That should do it:
FOR %%a IN (%Svcs%) DO (SC query %%a | FIND /i "RUNNING"
IF ERRORLEVEL 1 SC start %%a)
Language independent version.
#Echo Off
Set ServiceName=Jenkins
SC queryex "%ServiceName%"|Find "STATE"|Find /v "RUNNING">Nul&&(
echo %ServiceName% not running
echo Start %ServiceName%
Net start "%ServiceName%">nul||(
Echo "%ServiceName%" wont start
exit /b 1
)
echo "%ServiceName%" started
exit /b 0
)||(
echo "%ServiceName%" working
exit /b 0
)
I just found this thread and wanted to add to the discussion if the person doesn't want to use a batch file to restart services. In Windows there is an option if you go to Services, service properties, then recovery. Here you can set parameters for the service. Like to restart the service if the service stops. Also, you can even have a second fail attempt do something different as in restart the computer.
Cuando se use Windows en Español, el código debe quedar asi (when using Windows in Spanish, code is):
for /F "tokens=3 delims=: " %%H in ('sc query MYSERVICE ^| findstr " ESTADO"') do (
if /I "%%H" NEQ "RUNNING" (
REM Put your code you want to execute here
REM For example, the following line
net start MYSERVICE
)
)
Reemplazar MYSERVICE con el nombre del servicio que se desea procesar. Puedes ver el nombre del servicio viendo las propiedades del servicio. (Replace MYSERVICE with the name of the service to be processed. You can see the name of the service on service properties.)
For Windows server 2012 below is what worked for me. Replace only "SERVICENAME" with actual service name:
#ECHO OFF
SET SvcName=SERVICENAME
SC QUERYEX "%SvcName%" | FIND "STATE" | FIND /v "RUNNING" > NUL && (
ECHO %SvcName% is not running
ECHO START %SvcName%
NET START "%SvcName%" > NUL || (
ECHO "%SvcName%" wont start
EXIT /B 1
)
ECHO "%SvcName%" is started
EXIT /B 0
) || (
ECHO "%SvcName%" is running
EXIT /B 0
)
#echo off
color 1F
#sc query >%COMPUTERNAME%_START.TXT
find /I "AcPrfMgrSvc" %COMPUTERNAME%_START.TXT >nul
IF ERRORLEVEL 0 EXIT
IF ERRORLEVEL 1 NET START "AcPrfMgrSvc"
I also wanted an email sent if the service was started this way so added a bit to #Ic code just thought I would post it in case it helped anyone. I used SendMail but there are other command line options How to send a simple email from a Windows batch file?
set service=MyServiceName
for /F "tokens=3 delims=: " %%H in ('sc query %service% ^| findstr " STATE"') do (
if /I "%%H" NEQ "RUNNING" (
net start %service%
for /F "tokens=3 delims=: " %%H in ('sc query %service% ^| findstr " STATE"') do (
if /I "%%H" EQ "RUNNING" (
SendMail /smtpserver localhost /to me#mydomain.com /from watchdog#mydomain.com /subject Service Autostart Notification /body Autostart on service %service% succeded.
) else (
SendMail /smtpserver localhost /to me#mydomain.com /from watchdog#mydomain.com /subject Service Autostart Notification /body Autostart on service %service% failed.
)
)
)
)
Starting Service using Powershell script. You can link this to task scheduler and trigger it at intervals or as needed.
Create this as a PS1 file i.e. file with extension PS1 and then let this file be triggered from task scheduler.
To start stop service
in task scheduler if you are using it on server use this in arguments
-noprofile -executionpolicy bypass -file "C:\Service Restart Scripts\StopService.PS1"
verify by running the same on cmd if it works it should work on task scheduler also
$Password = "Enter_Your_Password"
$UserAccount = "Enter_Your_AccountInfor"
$MachineName = "Enter_Your_Machine_Name"
$ServiceList = #("test.SocketService","test.WcfServices","testDesktopService","testService")
$PasswordSecure = $Password | ConvertTo-SecureString -AsPlainText -Force
$Credential = new-object -typename System.Management.Automation.PSCredential -argumentlist $UserAccount, $PasswordSecure
$LogStartTime = Get-Date -Format "MM-dd-yyyy hh:mm:ss tt"
$FileDateTimeStamp = Get-Date -Format "MM-dd-yyyy_hh"
$LogFileName = "C:\Users\krakhil\Desktop\Powershell\Logs\StartService_$FileDateTimeStamp.txt"
#code to start the service
"`n####################################################################" > $LogFileName
"####################################################################" >> $LogFileName
"###################### STARTING SERVICE ##########################" >> $LogFileName
for($i=0;$i -le 3; $i++)
{
"`n`n" >> $LogFileName
$ServiceName = $ServiceList[$i]
"$LogStartTime => Service Name: $ServiceName" >> $LogFileName
Write-Output "`n####################################"
Write-Output "Starting Service - " $ServiceList[$i]
"$LogStartTime => Starting Service: $ServiceName" >> $LogFileName
Start-Service $ServiceList[$i]
$ServiceState = Get-Service | Where-Object {$_.Name -eq $ServiceList[$i]}
if($ServiceState.Status -eq "Running")
{
"$LogStartTime => Started Service Successfully: $ServiceName" >> $LogFileName
Write-Host "`n Service " $ServiceList[$i] " Started Successfully"
}
else
{
"$LogStartTime => Unable to Stop Service: $ServiceName" >> $LogFileName
Write-Output "`n Service didn't Start. Current State is - "
Write-Host $ServiceState.Status
}
}
#code to stop the service
"`n####################################################################" > $LogFileName
"####################################################################" >> $LogFileName
"###################### STOPPING SERVICE ##########################" >> $LogFileName
for($i=0;$i -le 3; $i++)
{
"`n`n" >> $LogFileName
$ServiceName = $ServiceList[$i]
"$LogStartTime => Service Name: $ServiceName" >> $LogFileName
Write-Output "`n####################################"
Write-Output "Stopping Service - " $ServiceList[$i]
"$LogStartTime => Stopping Service: $ServiceName" >> $LogFileName
Stop-Service $ServiceList[$i]
$ServiceState = Get-Service | Where-Object {$_.Name -eq $ServiceList[$i]}
if($ServiceState.Status -eq "Stopped")
{
"$LogStartTime => Stopped Service Successfully: $ServiceName" >> $LogFileName
Write-Host "`n Service " $ServiceList[$i] " Stopped Successfully"
}
else
{
"$LogStartTime => Unable to Stop Service: $ServiceName" >> $LogFileName
Write-Output "`nService didn't Stop. Current State is - "
Write-Host $ServiceState.Status
}
}
Related with the answer by #DanielSerrano, I've been recently bit by localization of the sc.exe command, namely in Spanish. My proposal is to pin-point the line and token which holds numerical service state and interpret it, which should be much more robust:
#echo off
rem TODO: change to the desired service name
set TARGET_SERVICE=w32time
set SERVICE_STATE=
rem Surgically target third line, as some locales (such as Spanish) translated the utility's output
for /F "skip=3 tokens=3" %%i in ('""%windir%\system32\sc.exe" query "%TARGET_SERVICE%" 2>nul"') do (
if not defined SERVICE_STATE set SERVICE_STATE=%%i
)
rem Process result
if not defined SERVICE_STATE (
echo ERROR: could not obtain service state!
) else (
rem NOTE: values correspond to "SERVICE_STATUS.dwCurrentState"
rem https://msdn.microsoft.com/en-us/library/windows/desktop/ms685996(v=vs.85).aspx
if not %SERVICE_STATE%==4 (
echo WARNING: service is not running
rem TODO: perform desired operation
rem net start "%TARGET_SERVICE%"
) else (
echo INFORMATION: service is running
)
)
Tested with:
Windows XP (32-bit) English
Windows 10 (32-bit) Spanish
Windows 10 (64-bit) English
#Echo off
Set ServiceName=wampapache64
SC queryex "%ServiceName%"|Find "STATE"|Find /v "RUNNING">Nul&&(
echo %ServiceName% not running
echo
Net start "%ServiceName%"
SC queryex "%ServiceName%"|Find "STATE"|Find /v "RUNNING">Nul&&(
Echo "%ServiceName%" wont start
)
echo "%ServiceName%" started
)||(
echo "%ServiceName%" was working and stopping
echo
Net stop "%ServiceName%"
)
pause
Maybe a much simpler way? Just adding to the list of answers here:
#for /f "tokens=1,* delims=: " %%a in ('sc queryex state=Inactive') do net start "%%b"

Resources