Run multiple commands and log output as one row on Windows batch - windows

i am trying to run a command 1 (wmic diskdrive get status) and command 2(wmic OS GET LocalDateTime /VALUE) and write output as one row to a file.
background information
I need to monitor SMART status with zabbix agent active. Windows version is one without powershell and i have very limited user rights there. I have about 50 machines that i need to monitor with it and a .bat skript is the most viable solution i have
background information ends
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "INTEXTFILE=c:\zabbix\smart_log.txt"
set "OUTTEXTFILE=c:\zabbix\smart_log_out.txt"
(wmic diskdrive get status
wmic OS GET LocalDateTime /VALUE) 1> %INTEXTFILE%
so far so good. I get a smart_log.txt with information:
Status
OK
LocalDateTime=20200114155453.199000+120
From here on i try to edit this file, to get all of this data as one row, but here i run into trouble.
i tried this answer but no luck.
setlocal EnableDelayedExpansion
set row=
for /f %%x in (%INTEXTFILE%) do set "row=!row! %%x"
>%OUTTEXTFILE% echo %row%
All i get there is :
ECHO is off.
My goal is to get a file with row:
Status OK (*maybe multiple OK OK OK if there are many disks) LocalDateTime=20200114155453.199000+120

#echo off
setlocal enabledelayedexpansion
set "line="
for /f "delims=" %%a in ('wmic diskdrive get status^|more') do set "line=!line!%%a,"
for /f "delims=" %%a in ('wmic OS GET LocalDateTime /value ^| more') do set line=!line!%%a
set "line=%line: =%"
echo %line:,= %
The output of wmic as an usual line ending (CRCRLF instead of CRLF) which complicates things. Piping it to more "corrects" those line endings. Then just concatenate the lines. To beautify (and adapt to your required format) build the strings with another delimiter (comma here),remove any spaces, and change the commas back to spaces.
Output on my system:
Status OK OK OK LocalDateTime=20200114165152.456000+060
Hint: changing to delims=. for the second for loop (the date), changes the output to:
Status OK OK OK LocalDateTime=20200114165555

Related

Windows batch command to create backup folder and replace folder

I need to backup an existing folder with date-time stamp and replace it (delete and recreate) with new content inside the folder.
Does anyone have a script to do this?
I tried the following code, where %ApplicationDeploymentFolderPath% = \\servername\foldername
IF EXIST %ApplicationDeploymentFolderPath%\Release (
REM Get current date time
#echo off
For /f "tokens=1-3 delims=/ " %%a in ('date /t') do (set mydate=%%c_%%b_%%a)
For /f "tokens=1-2 delims=/:" %%a in ('time /t') do (set mytime=%%a%%b)
set backup_folder=%mydate%_%mytime%
MD %ApplicationDeploymentFolderPath%\%backup_folder%
REM Copy current folder to backup folder
Copy %ApplicationDeploymentFolderPath%\Release %ApplicationDeploymentFolderPath%\%backup_folder%
REM Delete Existing Release folder
RD %ApplicationDeploymentFolderPath%\Release /S /Q
)
MD %ApplicationDeploymentFolderPath%\Release
The command date with parameter /T outputs the current date in format defined by configured country for current user account. Exactly the same date string can be accessed by referencing dynamic environment variable DATE for example with %DATE%.
The command time with parameter /T outputs the current time in format defined by configured country for current user account. Exactly the same time string can be accessed by referencing dynamic environment variable TIME for example with %TIME%.
What happens on execution of this command line?
For /f "tokens=1-3 delims=/ " %%a in ('date /t') do (set mydate=%%c_%%b_%%a)
for respectively cmd.exe processing the batch file starts in background one more command process using %ComSpec% /c with the command line between '. So executed in background is following with Windows installed in C:\Windows:
C:\Windows\System32\cmd.exe /c date /t
The output of command date to handle STDOUT of this command process in background is captured by FOR respectively Windows command processor instance executing the batch file.
The captured line is split up into three substrings using / as string delimiter assigned to the loop variables a, b and c which are concatenated together in reverse order with underscore as delimiter.
This task can be done much faster by replacing 'date /t' by "%DATE%". In this case FOR processes the date string expanded by already running cmd.exe on parsing this command line before executing FOR. So there is no starting of one more cmd.exe in background and capturing its output just to process the same date string which makes batch file execution a bit faster.
The same is true for 'time /t' which can be replaced by "%TIME%".
But the two FOR loops could be completely optimized away by using string substitution as described for example by answer on What does %date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2% mean? and region dependent date and time format is well known for example by running in a command prompt window:
echo %DATE% %TIME%
This command outputs on my computer with German date/time format according to configured country:
24.07.2019 20:15:29,90
It can be seen on this output that the original code would not work on my Windows computer with my account because of date string contains . and not / and time string contains a comma.
So better would be using a region independent solution as explained very detailed in answer on Why does %date% produce a different result in batch file executed as scheduled task? The disadvantage is that execution of wmic.exe takes much longer than cmd.exe needs to reformat date and time string to yyyy_MM_dd_HHmm. However, the batch file is executed most likely not very often per day, and so it does not really matter if execution to get date/time in this format takes some milliseconds or about one second.
Copying the entire folder is not really necessary in this case. It should be enough to rename it with:
ren "%ApplicationDeploymentFolderPath%\release" "%backup_folder%"
The command move could be also used if command ren cannot be used for unknown reasons.
However, the main problem is missing knowledge about how and when to use delayed expansion. Open a command prompt, run set /? and read the output help explaining on an IF and a FOR example delayed environment variable expansion.
The issue here is that backup_folder is not defined on executing the command lines referencing it with %backup_folder% because of all occurrences of %variable% are replaced by Windows command processor already on parsing entire command block starting here with ( on IF condition at top by current value of the referenced environment variable before executing the command IF.
So executed on existing release folder is:
set backup_folder=
MD \\servername\foldername\
REM Copy current folder to backup folder
Copy \\servername\foldername\Release \\servername\foldername\
REM Delete Existing Release folder
RD \\servername\foldername\Release /S /Q
This can be seen by debugging the batch file.
See also: How does the Windows Command Interpreter (CMD.EXE) parse scripts?
The solution is here avoiding the command block by changing the first IF condition.
Fast region dependent solution:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ApplicationDeploymentFolderPath=\\servername\foldername"
if not exist "%ApplicationDeploymentFolderPath%\Release\" goto CreateFolder
ren "%ApplicationDeploymentFolderPath%\Release" "%DATE:~-4%_%DATE:~-7,2%_%DATE:~-10,2%_%TIME:~0,2%%TIME:~3,2%"
:CreateFolder
md "%ApplicationDeploymentFolderPath%\Release"
endlocal
Slower region independent solution:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ApplicationDeploymentFolderPath=\\servername\foldername"
if not exist "%ApplicationDeploymentFolderPath%\Release\" goto CreateFolder
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "BackupDateTime=%%I"
set "BackupDateTime=%BackupDateTime:~0,4%_%BackupDateTime:~4,2%_%BackupDateTime:~6,2%_%BackupDateTime:~8,4%"
ren "%ApplicationDeploymentFolderPath%\Release" "%BackupDateTime%"
:CreateFolder
md "%ApplicationDeploymentFolderPath%\Release"
endlocal
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.
echo /?
endlocal /?
for /?
goto /?
if /?
md /?
ren /?
set /?
setlocal /?
wmic /?
wmic os /?
wmic os get /?
wmic os get localdatetime /?

capture result of wmic command to variable [duplicate]

This question already has answers here:
Set the value of a variable with the result of a command in a Windows batch file [duplicate]
(6 answers)
Assign command output to variable in batch file [duplicate]
(5 answers)
Closed 3 years ago.
I am trying to write a small batch script to install an app from a thumb drive. The problem is the drive letter changes when plugged into different machines depending on the available drive letters. I have the script to run the installation but would like to add a script to the beginning that would detect the assigned drive letter of my inserted thumb drive and store it in a variable that I could then substitute in the rest of the script for the drive letter to complete the installation.
I got the command to identify the assigned drive letter of the thumb drive which works on its own.
wmic logicaldisk where volumename="StacelandFlash" get name
Result: D: (correct)
But I can't seem to assign it to a variable.
set X=wmic logicaldisk where volumename="StacelandFlash" get name
echo X
Result: X
set X=wmic logicaldisk where volumename="StacelandFlash" get name
echo %X%
Result: wmic logicaldisk where volumename="StacelandFlash" get name
Firstly, to capture the output of a command to a variable use for /F. See for /? in a cmd console for full details. Example:
for /f "tokens=2 delims=:" %%I in ('find /c /v "" "notes.txt"') do (
set /a "linecount=%%I"
)
rem // %linecount% now contains the number of lines in notes.txt
Now, there are a couple of complications unique to capturing WMI query results. Firstly, your wmic command includes an equals sign, which will break a for /f. That part's easy enough to fix: either escape the = with a caret (e.g. ^=), or just surround the equation in quotation marks.
The next hurdle is a bit trickier. WMI results are encoded in a non-ANSI encoding (UCS-2 LE). Capturing the output of wmic also captures the output's encoding, resulting in the last character being moved to the beginning of the line or other unexpected behavior. The workaround for that is to use a second nested for /f to sanitize the value.
With all that in mind, I think this is what you're looking for:
#echo off
setlocal
for /f "tokens=2 delims==" %%I in (
'wmic logicaldisk where "volumename='StacelandFlash'" get name /value'
) do for /f "delims=" %%# in ("%%I") do set "driveletter=%%~#"
echo %driveletter%
Note: credit #Dave Benham for discovering this workaround.
You need to run the command within a For Loop.
#Echo Off
For /F "Skip=1 Delims=" %%A In ('
"WMIC LogicalDisk Where (VolumeName='StacelandFlash') Get Name"
') Do For %%B In (%%A) Do Set "USB=%%B"
Echo(%USB%
Timeout -1
I suppose the batch file executed is stored on the thumb drive. And this batch file is executed with a double click. Therefore all you need is:
set "DriveLetter=%~d0"
%~d0 references the drive of argument 0 which is the batch file name. Run in a command prompt window call /? for details on how to reference arguments of a batch file. %~d0 expands to D:, E:, ...

Why does for /f not ignore blank lines?

I'm trying to get a simple value from a for /f loop in a batch file.
Using the command wmic path win32_localtime get dayofweek gives me the output:
C:\sendemail>wmic path win32_localtime get dayofweek
DayOfWeek
1
C:\sendemail>
So, using the following batch script, I should be able to return a result of "1":
set cmd=wmic path win32_localtime get dayofweek
for /f "tokens=1 skip=1" %%Z in ('%cmd%') do set _myday=%%Z
echo Var is %_myday%
But I don't, it sets the variable at least twice, as seen here :
C:\sendemail>set cmd=wmic path win32_localtime get dayofweek
C:\sendemail>for /F "tokens=1 skip=1" %Z in ('wmic path win32_localtime get dayofweek') do set _myday=%Z
C:\sendemail>set _myday=1
:\sendemail>set _myday=
C:\sendemail>echo Var is
Var is
C:\sendemail>
At first, I wondered why, then I realised the loop is processing the two blank lines... which it shouldn't be. according to this: http://ss64.com/nt/for_cmd.html
Skip SKIP will skip processing a number of lines from the beginning of
the file. SKIP includes empty lines, but after the SKIP is complete,
FOR /F ignores (does not iterate) empty lines.
Either, it's not working normally, or those lines are not blank, and are filled with something...
At any rate, how do I get just the day of the week result out of this, and ignore the rest?
About the lines contents, yes, the output from wmic includes at the end of each line an additional carriage return character that is detected by the for command. That is the reason for your non empty lines.
You can try with
for /f "tokens=2 delims==" %%Z in ('
wmic path win32_localtime get dayofweek /value
') do for /f %%Y in ("%%Z") do set "_myday=%%Y"
The code is asking wmic to return the information in key=value format and using the equal sign to tokenize the input records. As we request only the second token, for will only process lines that return at least two tokens, lines having a key (first token), an equal sign (delimiter) and a value (second token)
But, the additional carriage return at the end of the line is also included in the value of the variable.
When the variable is used with normal syntax (%_myday%) it is not seen, but with delayed expansion (!_myday!) the additional carriage return will be echoed.
To remove this additional character from the variable, a second for command has been used in the posted code.
This slight midification will work:
#ECHO OFF
set cmd=wmic path win32_localtime get dayofweek
for /f "tokens=1 skip=1" %%Z in ('%cmd%') do (
set _myday=%%Z
GOTO BREAK
)
:BREAK
echo Var is %_myday%
PAUSE
Simply jump out of the loop after reading the desired line.
The above is a good approach and intitially I applied it successfully. Because of another need to capture a WMI variable and load into an environment variable, I wanted some that would be a native command line. I stumbled on this...
for /F "skip=2 tokens=2 delims=," %%i IN ('wmic timezone get Bias /format:csv') do (echo var Bias=%%i; > %TZONEDB%mid.htm )
The core is that using the CSV format makes skip work (no trailing blanks) and gives you easily parsed output.
ed g
I recently had the issue discussed here and whilst previous answers helped I wanted to retrieve several variables for subsequent testing. At first I used code similar to the following example (just not on explorer.exe and with different variable names. The following variable names have been deliberately chosen to match variable name restrictions in the later code).
setlocal
for /F "skip=2 tokens=2,3,4 delims=," %%a in ('
wmic /node:"MyFileServer-pc" process where Name^="explorer.exe"
get HandleCount^,
ParentProcessId^,
ThreadCount
/format:csv
') do (
set ProcHandleCount=%%a
set ProcParentProcessId=%%b
set ProcThreadCount=%%c
)
echo HandleCount %ProcHandleCount%, ParentProcessID %ProcParentProcessId%, ThreadCount %ProcThreadCount%
However, I dislike this solution as wmic always returns results in the order in which it internally iterates them disregarding the order in which they are presented to the wmic command, So the command wmic process where Name^="explorer.exe" get ThreadCount^,HandleCount^,ParentProcessId still returns ThreadCount last. This makes the previous code error prone when fetching multiple parameter value pairs and a hassle to get the ordering correct.
So instead I used the following code, which achieves the same result avoiding the need to care about the ordering of wmic parameters. It is also very easy to add additional parameters still with no need to care about ordering.
setlocal
for /F "tokens=1,2delims==" %%a in ('
wmic /node:"MyFileServer-pc" process where Name^="explorer.exe"
get HandleCount^,
ParentProcessId^,
ThreadCount
/value
') do #set Proc%%a^=%%b
echo HandleCount %ProcHandleCount%, ParentProcessID %ProcParentProcessId%, ThreadCount %ProcThreadCount%
In the above code, the variable naming is provided by the parameter of each returned parameter=value pair from wmic plus a fixed prefix, automatically matching returned parameter and value pairs. Note the use of a prefix, in this case 'Proc', without at least a one character prefix returned wmic blank lines cause errors. If different environment variable names are needed then the first solution allows freedom in naming and allows for shorter names but is more error prone when multiple values are required.
For what little it's worth... the following code avoids the variable prefixes and silences errors. More unique variable names would usually be desired.
(
for /F "tokens=1,2delims==" %%a in ('
wmic /node:"MyFileServer-pc" process where Name^="explorer.exe" get HandleCount^,ParentProcessId^,ThreadCount /value
') do #set %%a=%%b
) 2>nul
These examples use the wmic parameter /node:"MyFileServer-pc" to get remote process info but work equally for simpler wmic commands.
I always use this batch template when processing wmic output:
setlocal enableextensions enabledelayedexpansion
for /f "delims and such" %%a in ('wmic path win32_localtime get dayofweek 2^>nul ^<nul') do (
set VAR=%%a
CALL :RMCRLF VAR
if "!VAR!" NEQ "" (
rem If we get here, then it's a non-empty line
rem ... Do something ...
)
)
GOTO :EOF
:RMCRLF
:: All this does is clean stray carriage returns/line feeds off of a variable,
:: which wmic is notorious for injecting into its output.
set RMCRVAR=%1
IF NOT DEFINED RMCRVAR GOTO :EOF
CALL set %RMCRVAR%=%%%RMCRVAR%%%
GOTO :EOF
They key here is the "function" (if you will) :RMCRLF. It will strip the pesky carriage returns off of a variable so you can check for true emptiness of the string, and then decide to skip the empty line.

Redirecting appcmd output to a variable

I am trying to run the following:
FOR /F "tokens=* delims=" %A IN ('C:\windows\system32\inetsrv\appcmd.exe list app /site.name:"car" /xml | C:\windows\system32\inetsrv\appcmd.exe list vdir /vdir.name:"car/" /text:physicalPath') DO SET Variable=%A
But get the following error:
| was unxepected at this time
If the data must be piped from a process to the other, you need to escape the pipe character. It should be ^|
If what you need to do is execute both commands, replace the pipe character with ^&, the command concatenation operator, also escaped
For anyone reaches this, following is working example of batch file to get physical path of iis site , stored in a variable named 'physicalPath'.
This is based on #StuHarper explanations in comments.
#echo off
set site=stackoverflow.com
set site=%site:https://=%
set site=%site:http://=%
set site=%site: =%
set appcmd=%systemroot%\system32\inetsrv\AppCmd.exe
set xmlOutput=%appcmd% list app /site.name:%site% /path:"/" /xml
for /f "tokens=*" %%A in ('%xmlOutput% ^| %appcmd% list vdir /in /text:physicalPath') DO SET physcalPath=%%A
echo physcalPath: %physcalPath%
#echo on

Edit previous output to use in next command in batch

I need to pull a certain string from a command output in a batch file, I am trying to get the GUID of the active power scheme to query the settings.
Currently, my code looks like this:
FOR /F "delims=" %%i IN ('powercfg /getactivescheme') DO set scheme=%%i
powercfg /query %scheme%
but of course the powercfg /getactivescheme command adds other useless junk to the output, so I end up with
C:\Users\Richard\Desktop>FOR /F "delims=" %i IN ('powercfg /getactivescheme') DO
set scheme=%i
C:\Users\Richard\Desktop>set scheme=Power Scheme GUID: c0ea6ad3-6145-4447-a15e-5
fb97be69b98 (Energy Star)
Now, all I want to pull is: c0ea6ad3-6145-4447-a15e-5fb97be69b98 and truncate the Power Scheme GUID: and (Energy Star)
for input into the next command which is powercfg /query %scheme%
Any suggestions are welcome.
Thanks!
This gets the 4th token, separated by space/tabs
FOR /F "tokens=4" %%i IN ('powercfg /getactivescheme') DO set scheme=%%i

Resources