There is no column for User Name in the 'WMIC process' output - wmic

The Processes tab of the Windows Task Manager shows several columns with information, one of which being User Name of the user that owns the process.
Using the command WMIC process (from an administrator-started cmd.exe) gives me the same sort of information, but I can not find any column for the user name. How can I find out which user started the process with WMIC?

Owner can be retrieved using GetOwner method on win32_process class instances.
I would suggest using PowerShell for that, where it's pretty simple:
Get-WmiObject -Class Win32_Process |
Select-Object Name, #{
Name = 'Owner'
Expression = {
$_.GetOwner().User
}
}
If you have to use wmic, than you can hack your way through by mixing results of:
wmic process get Name
...with call GetOwner e.g:
#echo off
echo Domain\User,Machine,ProcessName,ProcessID,WorkingSetSize
(for /f "skip=2 tokens=2 delims=, eol= " %%P in ('wmic process get ProcessId /format:csv') do #call :AddOwner %%P) 2> nul
goto :EOF
:AddOwner
SET Process=%1
(for /f "skip=5 tokens=1,2 delims==; " %%O in ('wmic process WHERE ProcessID^=%Process% Call GetOwner') do #call :BuildOwner %%O %%P) > nul
for /f "skip=1 tokens=* eol= " %%L in ('wmic process WHERE ProcessID^=%Process% GET Name^, ProcessID^, WorkingSetSize /format:csv') do #SET INFO=%%L
echo %DOMAIN%\%USER%,%INFO%
goto :EOF
:BuildOwner
SET PARAM=%1
SET VALUE=%~2
IF [%PARAM%]==[Domain] SET DOMAIN=%VALUE%
IF [%PARAM%]==[User] SET USER=%VALUE%
goto :EOF

To get the owners of all of the taskeng.exe processes (Windows Task Scheduler processes for individual tasks), run this from an 'admin' Command Prompt window:
wmic process where "name='taskeng.exe'" call GetOwner

Related

Batch run as administrator all users

I'm working on an unattended automated install of SQL 2008, 2012 and 2014 which so far is working with my batch command. The one issue I'm encountering is that in order to use a core function found in each program (management studio) properly on Windows 10, it needs to run as an admin.
In Windows 10 this can be done manually by opening the file location of the program shortcut - right click - properties - "compatibility" tab - "change settings for all users" - check the box "run this program as an administrator". Is there a way to have a batch command check that box? This way staff won't need to manually run it as an admin each time, it'll just open Management Studio automatically as an admin.
My batch command can be found below to automate the install. The beginning finds the installation files, sets the sa password to whatever I want and pulls the custom settings for the SQL installer from the configurationfile.ini file. I need the "run as an admin" for all users to run after all that.
start "" "%~dp0SQL2008\setup.exe" /SAPWD="XXXXXXXX" /ConfigurationFile="ConfigurationFile.ini" /quiet
#echo off
setlocal
call :reg_compat "C:\User\Test\test.exe" "WINXPSP3 RUNASADMIN"
if errorlevel 1 echo Registry write failed.
exit /b
:reg_compat fullpath, reg_data
setlocal
set "reg_key=HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"
set "reg_data="
:: Get current registry data.
for /f "tokens=1,2*" %%A in ('2^>nul reg query "%reg_key%" /v "%~1"') do (
if /i "%%~A" == "%~1" set "reg_data=%%~C"
)
:: Write new entry and exit.
if not defined reg_data (
>nul reg add "%reg_key%" /v "%~1" /d "%~2" /f
if errorlevel 1 exit /b 1
exit /b 0
)
:: Copy original registry data.
set "reg_data_original=%reg_data%"
:: Append new data if not in original registry data.
for %%A in (%~2) do (
set "value_exist="
for %%B in (%reg_data_original%) do (
if "%%~A" == "%%~B" set "value_exist=1"
)
if not defined value_exist (
call set "reg_data=%%reg_data%% %%~A"
)
)
:: Continue only if registry data is modified.
if "%reg_data_original%" == "%reg_data%" exit /b 0
:: Write modified entry and exit.
>nul reg add "%reg_key%" /v "%~1" /d "%reg_data%" /f
if errorlevel 1 exit /b 2
exit /b 0
The code is set to write a test entry. The test entry will add
Windows XP SP3 and Run As Admin compatibility for that filepath.
For actual use change the arguments to call the label :reg_compat
with the fullpath to the file as the 1st argument and the
reg_data compatibility arguments as the 2nd argument. The
compatibility arguments are uppercase and separated with a space.
The label :reg_compat will write new entries or update entries with
adding new compatibility arguments. The errorlevel from the called
label is set to not 0 if reg add fails.
Minimal code instead if you need just that:
#echo off
setlocal
set "reg_key=HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"
reg add "%reg_key%" /v "C:\User\Test\test.exe" /d "WINXPSP3 RUNASADMIN" /f

Enter drives for backup via keyboard

What I am trying to do is a backup by cmd commands but the problem is when I am taking the USB backup to another PC to back up the drive names are different.
For example when I do:
XCOPY G:\*.BMP X:\ /h/i/c/k/e/y/r/d
In the other computer the drives will not be G and X.
What I am seeking to do is if it is possible to make a program that I can enter with the keyboard what drive I want to backup and to what drive.
For example:
XCOPY "driver name keyboard input":\*.BMP "driver name keyboard input":/" /h/i/c/k/e/y/r/d
Yes, it can be done using a PowerShell or batch-file script (cmd tag seems to imply Windows OS).
Let's choose the latter. Next batch-file code snippet would do the same as the command in question: XCOPY G:*.BMP X:\ /h/i/c/k/e/y/r/d:
set "DriveIn=G"
set "DriveOu=X"
XCOPY %DriveIn%:*.BMP %DriveOu%:\ /h/i/c/k/e/y/r/d
Instead of hard-coded DriveIn and DriveOu, you can prompt for user input:
set /P "DriveIn=please choose SOURCE drive letter "
set /P "DriveOu=please choose TARGET drive letter "
XCOPY %DriveIn%:*.BMP %DriveOu%:\ /h/i/c/k/e/y/r/d
Hints for (necessary!) validity checks:
:dfromscratch
set "DriveIn="
set "DriveOu="
:dsource
set /P "DriveIn=please choose SOURCE drive letter "
rem basic validity check
if not defined DriveIn goto :dsource
if not exist "%DriveIn%:\" goto :dsource
:dtarget
set /P "DriveOu=please choose TARGET drive letter "
rem basic validity check
if not defined DriveOu goto :dtarget
if not exist "%DriveOu%:\" goto :dtarget
if /I "%DriveIn%"=="%DriveOu%" goto :dfromscratch
rem involve more validity check here!!!
XCOPY %DriveIn%:*.BMP %DriveOu%:\ /h/i/c/k/e/y/r/d
Some hints for (more) validity checks.
To show available disk drives:
wmic logicaldisk get Description, DeviceID, DriveType, FileSystem, VolumeName
To get a list of available disk drives, use for /F loop (note %%G in a batch script):
for /F %%G in ('
wmic logicaldisk where "DriveType != 5" get DeviceID^, DriveType^|find ":"
') do echo %%G
or next oneliner (note %G in cmd): copy&paste into an open cmd window:
for /F %G in ('wmic logicaldisk where "DriveType != 5" get DeviceID^, DriveType^|find ":"') do #echo %G
Another approach (only a draft, needs more elaboration): build a list of available drive letters %drives% and use choice command instead of set /P:
set "drives=GX"
choice /C %drives% /M "Select SOURCE drive letter"

How to remember for-loops iterating variable inside function and if-statements in batch-file?

I want to automate the execution of several bdf-files in a folder. But, I can only have one instance of my application running at the time (due to my license for the applicetion).
I want my code to do like below:
FOR ALL *.bdf files in folder DO THIS:
CHECK IF application is running
IF running, START index.bdf (with default application)
IF NOT running, WAIT a little, then go to "CHECK IF" again
Continue For-loop
My code look like this:
#ECHO OFF
TITLE AutorunNastran
FOR %%f IN (*.bdf) DO (
:loopbody
tasklist /nh /fi "imagename eq nastran.exe" | find /i "nastran.exe" >nul && (
echo Nastran is running
TIMEOUT 5
goto loopbody
) || (
echo Nastran is not running, a new bdf-file will be executed
START %%f
)
)
PAUSE
The first .bdf file will be executed, but it seems that my variable index.bdf=%%f is forgotten the second and third time the program is running through i-statements.
I'm new to batch-files, but I've tried to find answer on google, and think maybe I need to have this command "Setlocal EnableDelayedExpansion", but it has'nt worked for me so far.
Some help would be much appreciated!
Executing a goto inside a loop cancels the loop. Try with
#echo off
setlocal enableextensions disabledelayedexpansion
set "app=nastran.exe"
for %%a in (*.bdf) do (
cmd /q /c"(for /l %%z in (0) do (tasklist /nh /fi "imagename eq %app%" | find /i "%app%" && timeout 5 || exit /b)) >nul 2>&1"
start "" "%%~a"
)
Here the loop is moved to a separate cmd instance that will keep an infinite loop until the app is not found in tasklist

OS Name Variable

I would like to run a script where I can get the windows Name and version of the system of all computers running in the company, put it in a text fil. Then make a system variable out of my windows name . I know what to run but where I am running into an issue is a place holder. so here is my code:
:OS_NAME
Set OS_NAME= systeminfo | find "OS Name"
:OS_Ver
Set OS_Version= systeminfo | findstr /B /C:"OS Version"
systeminfo | findstr /B /C:"OS Name" /C:"OS Version" /c:"BIOS Version" | >> G:\Directory\%Computername%
:OS_Arch
IF exist "%programfiles(x86)% (SET OS_ARCH=X64)
Else (SET OS_ARCH=X86)
:Win_7
systeminfo | find "Microsoft Windows 7" > nul
if %ERRORLEVEL% == 0 (
goto ver_7)
:Ver_7
Set Win7= systeminfo | find "Microsoft Windows 7"
Echo %computername% is running %WIN7% in %OS_ARCH% Environment >> G:\Directory\Win7Comps.txt
So basically I would like a place holder for the results of Systeminfo which i can refer to and parse it my SET command when I am making my system variables.
Thanks, any help would be appreaciated.
Not exactly sure what you're looking for, but the biggest problem I see is that systeminfo takes forever to run and returns much more information than you're looking for. You'd be better off capturing wmi queries using wmic. This is basically a rewrite of your example script, just using wmic rather than systeminfo. It should be much, much faster.
#echo off
setlocal
set prefix=G:\Directory
for /f "usebackq tokens=1,2 delims==|" %%I in (`wmic os get name^,version /format:list`) do 2>NUL set "%%I=%%J"
for /f "tokens=2 delims==" %%I in ('wmic bios get version /format:list') do set "bios=%%I"
for /f "tokens=2 delims==" %%I in ('wmic computersystem get model /format:list') do set "model=%%I"
>>"%prefix%\%COMPUTERNAME%" echo OS Name: %name%
>>"%prefix%\%COMPUTERNAME%" echo OS Version: %version%
>>"%prefix%\%COMPUTERNAME%" echo PC Model: %model%
>>"%prefix%\%COMPUTERNAME%" echo BIOS Version: %bios%
if defined PROGRAMFILES(x86) (set arch=X64) else set arch=X86
if "%name%" neq "%name:Windows 8=%" (
set out=%prefix%\Win8Comps.txt
) else if "%name%" neq "%name:Windows 7=%" (
set out=%prefix%\Win7Comps.txt
) else if "%name%" neq "%name:Windows Vista=%" (
set out=%prefix%\WinVistaComps.txt
) else if "%name%" neq "%name:Windows XP=%" (
set out=%prefix%\WinXPComps.txt
)
>>"%out%" echo %COMPUTERNAME% is running %name% in %arch% environment
Type wmic /? for more info, and try wmic computersystem get /? or similar to see a list of items that can be queried under each class.
wmic is the Swiss Army knife of Windows. Fun fact: you can even use wmic to generate a web page table of installed software on a remote system.
You are giving batch a little too much credit. If you ran echo %OS_NAME% it would literally echo systeminfo | find "OS Name". Variables in batch will always just expand, it won't process any further. This will also more than likely try to run find "OS Name" when you try to set the variable as the | is not escaped nor enclosed in double quotes.
If you want to set the output of a command to a value you have to capture it in a for statement like this:
for /f "tokens=2 delims=:" %%a in ('systeminfo ^| find "OS Name"') do set OS_Name=%%a
Then remove the leading spaces like this: (there is probably a better way to do this)
for /f "tokens=* delims= " %%a in ("%OS_Name%") do set OS_Name=%%a
A few things to note here. 1.) "tokens=2 delims=:" is setting the delimiter to : and it is selecting the second section only, which will pull only the part you want. 2.) the | is escaped with a ^, this needs to be done in for loops or anything after that will attempt to execute as seperate commands. 3.) "tokens=* delims= " The token here is * which is all tokens.
A few other problems I found.
systeminfo | findstr /B /C:"OS Name" /C:"OS Version" /c:"BIOS Version" | >> G:\Directory\%Computername%
This has an extra | character at the end.
IF exist "%programfiles(x86)% (SET OS_ARCH=X64)
Else (SET OS_ARCH=X86)
Two problems here, you didn't finish the double quote around the path, and else has to be on the same line as the ending parentheses of the if statement, like this:
IF exist "%programfiles(x86)% (SET OS_ARCH=X64
) Else (SET OS_ARCH=X86)
Otherwise it tries to process else as it's own command

Identify running instances of a batch file

These are not working for me.
Any help to definitelly corret the four examples below ?
The EXAMPLE01 just echoes "continue", even if I have three CMD.exe opened.
---------- EXAMPLE 01 ------------
#echo off
wmic process where name="cmd.exe" | find "cmd.exe" /c
SET ERRORLEVEL=value if "%value%" GTR 1 (
ECHO This batch is not first
ECHO quitting ...
)
if "%value%" LSS 2 ECHO continue
I am getting the "unexpected i error" message in the EXAMPLE 02!
----------- EXAMPLE 02 -------
#echo off
FOR /F "usebackq tokens=2" %i IN (`tasklist ^| findstr /r /b "cmd.exe"`)
DO taskkill /pid %%i
I am getting the "is first" message in the EXAMPLE 03, even with three CMD.exe opened!
----------- EXAMPLE 03 -------
#echo off
wmic process where name="cmd.exe" | find "cmd.exe" /c
if "%errorlevel%" LEQ 1 echo CMD is first
if "%errorlevel%" GTR 1 echo CMD is already running
It is also possible that I will not have access to the Wmic command at work, so, another possibility is found in the EXAMPLE 04 ... but to no avail.
----------- EXAMPLE 04 -------
#echo off
Tasklist /FI "IMAGENAME eq cmd.exe" 2>NUL | find /I /N "cmd.exe">NUL
if "%ERRORLEVEL%"==0 do (goto Use) else (goto Cont)
:Cont
ECHO Only one instance running
pause
:Use
echo Application running already. Close this window
Kind regards,
Maleck
wmz identified a number of errors in the OP's code, and also has an excellent suggestion to use a lock file for concurrency control.
Below is a robust batch solution that uses a lock file to prevent multiple instances of the batch file from running at the same time. It uses a temporary lock file for the concurrency control. The mere presence of the lock file does NOT stop the script from running. The script will only fail if another process has an exclusive lock on the lock file. This is important in case the script should crash or be killed without deleting the lock file. The next process to run the script will still succeed because the file is no longer locked.
This script assumes the script is installed on a local drive. It allows only one instance for the entire machine. There are lots of variations to control the amount of concurrency allowed. For example, incorporating the %USERNAME% into the lock file name would allow one instance per user in a network environment. Incorporating %COMPUTERNAME% in the name would allow one instance per machine in a network environment.
#echo off
setlocal
:: save parameters to variables here as needed
set "param1=%~1"
:: etc.
:: Redirect an unused file handle for an entire code block to a lock file.
:: The batch file will maintain a lock on the file until the code block
:: ends or until the process is killed. The main code is called from
:: within the block. The first process to run this script will succeed.
:: Subsequent attempts will fail as long as the original is still running.
set "started="
9>"%~f0.lock" (
set "started=1"
call :start
)
if defined started (
del "%~f0.lock" >nul 2>nul
) else (
echo Process aborted: "%~f0" is already running
)
exit /b
:start
:: The main program appears below
:: All this script does is PAUSE so we can see what happens if
:: the script is run multiple times simultaneously.
pause
exit /b
EDIT
The error message "The process cannot access the file because it is being used by another process." can be suppressed by redirecting stderr output to nul in an outer block.
2>nul (
9>"%~f0.lock" (
set "started=1"
call :start
)
)
The problem with the above is that all error messages for the main program will also be suppressed. That can be fixed by 1st saving the current definition of stderr to another unused file handle, and then adding yet another inner block that redirects stderr back to the saved file handle.
8>&2 2>nul (
9>"%~f0.lock" (
2>&8 (
set "started=1"
call :start
)
)
)
You do not set value anywhere. Even if you did it would not work as variables are expanded on parse. You would need to use delayed expansion if you need to both set and test variable against what was set. Quotes on comparison are not required (see 3). help set will show you info delayed expansion.
%i should be %%i. DO taskkill /pid %%i must be on same line as rest of for command. You also use findstr in regex mode, which means it will search for cmd[any character]exe
You use string (lexical) comparison: "1" leq 1 is true (as "4" leq 1 also is...). Use errorlevel 1 which is equivalent to errorlevel equal or greater than 1. help if shows syntax
Same as 3 plus do in do (goto Use) should be removed. %errorlevel%==0 would work, but it's normally advised to use errorlevel number.
How to check if there is only 1 cmd running:
#echo off
for /f %%i in ('Tasklist /FI "IMAGENAME eq cmd.exe" 2^>NUL' ^| find /I /c "cmd.exe"') do (
if %%i leq 2 (echo only one instance) else (echo More than one instance)
)
Note: it's just an example. I do not recommend this as a real method of concurrency control. I would rather use lock file for that.