I have a script that performs upgrade to a database.
The script also logs before the upgrade starts, but when this script tries to access the log file it hangs for an indefinte time.
The line that's causing the issue is:
%LOGMESSAGE% Start update %UPDVERSION% .
LOGMESSAGE is a cmd file which is as follow:
SETLOCAL enabledelayedexpansion
SET /A FT=500
FOR /F "skip=1 tokens=1-6" %%A IN ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Month^,Second^,Year
/Format:table') DO (
IF NOT !FT!==500 GOTO proceed
SET FD=%%F-%%D-%%A
SET FT=%%B:%%C:%%E
:proceed
SET /A FX="DS"
)
endlocal
The main function of the LOGMESSAGE is to get the current system time.
The log file into which the scripts writes to has no issues and so does the function LOGMESSAGE, as the log file is written many times before the line %LOGMESSAGE% Start update %UPDVERSION% . is called. The script seems to work without any hassle on many other computer, but I am having an issue with one server, the server is running windows server 2003 R2 SP2.
Any idea what the issue might be?
try this:
#ECHO OFF &SETLOCAL ENABLEDELAYEDEXPANSION
SET /A FT=500
FOR /F "tokens=1-6" %%A IN ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Month^,Second^,Year /Format:table^|find "20"') DO (
IF NOT !FT!==500 GOTO proceed
SET FD=%%F-%%D-%%A
SET FT=%%B:%%C:%%E
:proceed
SET /A FX="DS"
)
endlocal
If you just want to get the date and time, you can use
DATE /T
TIME /T
instead of WMI.
Three comments:
.cmd files are rare - normally, .bat files are used. There are minor differences between the two, but I'm not sure whether they would be relevant (I can no longer remember them...)
It's perfectly legitimate to have have a line-break just before the opening single-quote and just after the closing. A break in the middle of the command, I'm not sure about - not saying this IS a problem, but it's so easily eliminated.
I'm no longer sure, since I rarely use XP any more, and never S2003 - but ISTR there were problems with having a label within a block statement. The label seemed to end the block.
So - I'd suggest
running the routine as a .bat instead of a .cmd, using
for...(
'whatever whatever...'
) do (
structure, and implementing whatever tomfoolery is happening about FD, FT and FX within a subroutine.
Related
I have this batch to compare the modified dates of two files that works great on an old version of Windows 10 '10.0.10240' but not at all on a slightly newer version of Windows 10 '10.0.17134.1304', so what am I missing here? Does the slightly newer version of Windows 10 require the installation of any additional software?
The batch file is as follows:
#echo off
Set _File1="D:\Steam\steamapps\common\dota 2 beta\game\dota\bin\win64\client.dll"
Set _File2="D:\Programs\client.dll"
For /F "Delims=" %%I In ('xcopy /DHYL %_File1% %_File2% ^|Findstr /I "File"') Do set /a _Newer=%%I 2>Nul
If %_Newer%==1 (Set _Newer=%_File1%) Else (Set _Newer=%_File2%)
Echo The newest file is %_Newer%
Pause
works perfectly on 'Windows 10.0.10240' but fails to start on 'Windows 10.0.17134.1304' (only opens for a fraction of a second and then immediately closes)
By including the /Q option with XCOPY, there is no need to pipe through to FINDSTR.
You could therefore use this modification of your chosen FOR /F idea:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "_File1=D:\Steam\steamapps\common\dota 2 beta\game\dota\bin\win64\client.dll"
Set "_File2=D:\Programs\client.dll"
For %%G In ("%_File1%" "%_File2%") Do If Not Exist %%G (Echo A file is missing.& GoTo :EndIt)
Set "_Newer="
For /F %%G In ('%SystemRoot%\System32\xcopy.exe "%_File1%" "%_File2%" /DHLQY 2^>NUL') Do If %%G Equ 1 (Set "_Newer=%_File1%") Else Set "_Newer=%_File2% or neither"
If Not Defined _Newer (Echo An error occurred.& GoTo :EndIt)
Echo %_Newer% is the newer file.
:EndIt
Pause
EndLocal
GoTo :EOF
I have added some additional 'debugging' code to help you to determine what happens on each different system version, and to correct your assumption that 0 means that %_File2% is newer, when both may be datestamped the same.
Your problem appears to be that the response from xcopy, as filtered through findstr will either be 1 File(s) or 0 File(s) and this string is applied to newer
Your if statement then is resolved to
if 1 File(s)==1 ....
So you get a syntax error.
To resolve, remove the "delims=" from the for /f which will then use the default delimiters which includes Space and return the default first token, which is 1 or 0.
Only real puzzle is how it ever worked in your earlier Win10.
Obligatory sermon:
Use set "var=value" for setting string values - this avoids problems caused by trailing spaces. Don't assign a terminal \, Space or " - build pathnames from the elements - counterintuitively, it is likely to make the process easier.
When you use the point-click-and-giggle method of executing a batch, the batch window will close if a syntax-error is found or the script runs to completion. You can put a pause after statements and home in on the error, but better to open a 'command prompt' and run your batch from there so that the window remains open and any (error) messages will be displayed.
--- Revision ---
After a little experimentation, I looked a little closer at the code.
The "delims=" delivers the strings I described - BUT - the set /a (rather than what I expected, set) would grab that number, then fail (hence the 2>nul)
So - what I found is that if file2 does not exist, the script stops waiting for a response to xcopy's question "is file2 a file or directory?`
This may be the cause of the failure on clicking the batch.
So - I'd suggest
set "_newer="
if exist %_file1% set "newer=1"&if exist %_file2% for ...
if defined _newer (if _%_newer%==1....) else Echo %_file1% not found
I have been fighting with this for 2 hours now. I need to set a variable from a second line output of a command. I found this code here at stackoverfglow:
for /f "skip=1delims=" %%a in (
'%SystemRoot%\System32\wbem\wmic.exe CSPRODUCT GET NAME'
) do set sid=%%a&goto next
The output for wmic.exe CSPRODUCT GET NAME is:
Name
PowerEdge T610
but when I run the batch file I get:
>set sid=PowerEdge T610
T610 was unexpected at this time.
I tried putting quotes everywhere.
I'm doing this script to check if the computer is a VirtualBox o VMware machine, the idea is to set a variable with the output of that command then compare it to "VirtualBox". But physical computers have product name with spaces.
THANKS!
I would offer the following method, as it will capture the full string without any potentially unwanted trailing space characters:
#For /F Tokens^=6Delims^=^" %%G In ('""%__AppDir__%wbem\WMIC.exe" CSProduct Get Name /Format:"MOF" 2>Nul"')Do #Set "SID=%%G"
Note: If you are using Windows 7, the use of some output formats may be broken, as the relevant .xsl stylesheets are located inside a language specific subdirectory which is not read by default. This issue can be worked around, by copying/moving or creating links to, all of the .xsl files one level higher, i.e. inside the wbem directory. (see potential alternative fix at the foot of this answer)
Alternatively, you could omit the for-loop completely, and instead of defining a variable, just use conditional statements:
#("%__AppDir__%wbem\WMIC.exe" CSProduct Where "Name Like '%%VirtualBox%%' Or Name Like '%%VMWare%%'" List Instance /Format:"List" 2>NUL|"%__AppDir__%find.exe" "__">NUL&&(Echo Virtual)||Echo Physical)&Pause
Note: In this example please ensure that you replace VirtualBox and/or VMWare as/if necessary. You can obviously replace the respective Echo commands with your chosen commands after testing.
Alternatively, for the actual process you need it for, based upon your comments, the following should also suffice:
#("%__AppDir__%wbem\WMIC.exe" ComputerSystem Where "Model Like '%%Virtual%%'" Get /Value 2>NUL|"%__AppDir__%find.exe" "=">NUL&&(Echo Virtual)||Echo Physical)&Pause
This should pick up a Hyper-V machine, (Model:Virtual Machine), a VMware machine, (Model:VMware Virtual Platform), and an Oracle VM, (Model:VirtualBox).
Here is a modified batch-file, which is intended to fix the potential Format issue in windows-7, as mentioned in my first example above:
#Echo Off
Set "XSL=MOF"
For /F "Delims==" %%G In ('"Set SID 2>NUL"')Do Set "%%G="
For /F "EOL=MDelims=" %%G In (
'""%__AppDir__%wbem\WMIC.exe" OS Get MUILanguages,Version 2>NUL"'
)Do For /F Tokens^=2^,4-5Delims^=.^"^ %%H In ("%%G"
)Do If %%I Equ 6 If %%J Equ 1 (Call :Target "%__APPDIR__%wbem\%%H\%XSL%"
)Else Call :Target "%XSL%"
Set SID 2>NUL&&Pause
GoTo :EOF
:Target
For /F Tokens^=6Delims^=^" %%G In (
'""%__AppDir__%wbem\WMIC.exe" CSProduct Get Name /Format:"%~1" 2>Nul"'
)Do Set "SID=%%G"
Line 9 is where you'd place your code, replacing mine which was added just for demonstration purposes.
I'm a computational biologist and I'm trying to run large batches of similar code with a single command, but my implementation has hit a brick wall.
I'm using the NEURON simulation environment, which uses MinGW for its Windows interface, which is where my research has shown my problem arises.
Currently, I am using a batch file to run all of these similar pieces of code, to iterate across the "collection" subfolders:
#echo off
for /D %%a in ("%cd%\all_cells\cell_*.*") do cd "%%a\sim1\" & START neuron sim.hoc
The problem arises when I have more than 32 subfolders; the additional instances won't run and will error with a "console device allocation failure: too many consoles" error.
My research has shown me that this is a known problem with Cygwin/MinGW.
However, working around this manually (ensuring that there is no more than 32 "collection" folders) is extremely time consuming when I am now dealing with hundreds of instances (each refers to a simulated cell and I want to gather statistics on hundreds of them), so I am trying to find a solution.
That said, I am terrible at writing batch files (I'm a terrible programmer who is used to scientific languages) and I can't figure out how to code around this.
It would be great if someone could help me either find a way around the 32 limit, or failing that, help me write a batch file that would do this:
-iterate over up to 32 folders
-wait for the instances to finish
-do it again for the next 32, until I reach the end of the folder.
I have tried using the /wait command to do them one at a time, but it still opens all 32. (And this wouldn't be ideal as I'd like to use all 16 cores I have.
The following is adapted from https://stackoverflow.com/a/11715437/1012053, which shows how to run any number of processes while limiting the total number run simultaneously in parallel. See that post for some explanation, though the code below is fairly well documented with comments.
I've eliminated the /O option and the code to work with PSEXEC from the original script.
The script below runs everything in one window - the output of each process is captured to a temporary lock file, and when finished, the full output of each process is typed to the screen, without any interleaving of process output. The start and end times of each process are also displayed. Of course you can redirect the output of the master script if you want to capture everything to a single file.
I've limited the total number of parallel processes to 16 - of course you can easily modify that limit.
The code will not work as written if any of your folder paths include the ! character. This could be fixed with a bit of extra code.
Other than that, the code should work, provided I haven't made any silly mistakes. I did not test this script, although the script it was derived from has been thoroughly tested.
#echo off
setlocal enableDelayedExpansion
:: Define the maximum number of parallel processes to run.
set "maxProc=16"
:: Get a unique base lock name for this particular instantiation.
:: Incorporate a timestamp from WMIC if possible, but don't fail if
:: WMIC not available. Also incorporate a random number.
set "lock="
for /f "skip=1 delims=-+ " %%T in ('2^>nul wmic os get localdatetime') do (
set "lock=%%T"
goto :break
)
:break
set "lock=%temp%\lock%lock%_%random%_"
:: Initialize the counters
set /a "startCount=0, endCount=0"
:: Clear any existing end flags
for /l %%N in (1 1 %maxProc%) do set "endProc%%N="
:: Launch the commands in a loop
set launch=1
for /D %%A in ("%cd%\all_cells\cell_*.*") do (
if !startCount! lss %maxProc% (
set /a "startCount+=1, nextProc=startCount"
) else (
call :wait
)
set "cmd!nextProc!=%%A"
echo -------------------------------------------------------------------------------
echo !time! - proc!nextProc!: starting %%A
2>nul del %lock%!nextProc!
cd "%%A\sim1\"
%= Redirect the output to the lock file and execute the command. The CMD process =%
%= will maintain an exclusive lock on the lock file until the process ends. =%
start /b "" cmd /c 1^>"%lock%!nextProc!" 2^>^&1 neuron sim.hoc
)
set "launch="
:wait
:: Wait for procs to finish in a loop
:: If still launching then return as soon as a proc ends
:: else wait for all procs to finish
:: redirect stderr to null to suppress any error message if redirection
:: within the loop fails.
for /l %%N in (1 1 %startCount%) do 2>nul (
%= Redirect an unused file handle to the lock file. If the process is =%
%= still running then redirection will fail and the IF body will not run =%
if not defined endProc%%N if exist "%lock%%%N" 9>>"%lock%%%N" (
%= Made it inside the IF body so the process must have finished =%
echo ===============================================================================
echo !time! - proc%%N: finished !cmd%%N!
type "%lock%%%N"
if defined launch (
set nextProc=%%N
exit /b
)
set /a "endCount+=1, endProc%%N=1"
)
)
if %endCount% lss %startCount% (
timeout 1 /nobreak >nul
goto :wait
)
2>nul del %lock%*
echo ===============================================================================
echo Thats all folks^^!
You could install screen or tmux in cygwin.
Then you can start all neuron instances in a screen/tmux session.
They will not open a new window, so there is no limit anymore.
First thank you for this great site! I've learned lots of batch scripting from here, but finally got stuck. I was tasked to write a script that will go out and check a specific registry keyword and change the ones that are not correct, on all PCs on the network.
#echo off
SetLocal EnableDelayedExpansion
FOR /F %%a in (C:\batchFiles\computers.txt) DO (
FOR /F "tokens=3" %%b in (reg query "\\%%a\HKLM\SOFTWARE\some\any" /v "Forms Path") do set "var=%%b"
if "%var%" == "\\server\folder\forms\path"
echo %%a was correct
pause
if "%var%" NEQ "\\server\folder\forms\path"
echo %%a was not correct
pause
)
My boss tasked me with this not to long ago and its a little above my head, so i'm trying to learn on the fly. I tried with %errorlevel% and couldn't get it to do what I wanted either.
I had all of my PC names listed in C:\batchFiles\computers.txt. The REG_SZ key from "Forms Path" is a folder located on a network drive. Right now it says that the syntax is incorrect.
If you can understand what i'm trying to do, and have a better suggestion, I'm all ears! Oh and I'd like to output ALL of the results to a text file so I know which PCs were changed, which ones had it correct, and which ones the script couldn't reach.
Thank you so much for your time!
You enabled delayed environment variable expansion, but do not use it. %var% must be written as !var! to make use of delayed expansion as required here.
The syntax used on both if conditions is also not correct.
The registry query output by reg.exe on my computer running Windows XP is:
! REG.EXE VERSION 3.0
HKEY_LOCAL_MACHINE\SOFTWARE\some\any
Forms Path REG_SZ \\server\folder\forms\path
There is first a blank line, next a line with version of reg.exe, one more blank line, a line with registry key and finally on fifth line the data of interest. Therefore I used in the batch code below skip=4 to speed it up. However, the inner loop would produce the right result also without skip=4 and therefore parsing all 5 lines.
Important is the last line. The inner loop separates by spaces. As the name of the registry value contains also a space character, the first two tokens are for Forms and Path. And the third token is REG_SZ.
The rest of the line after the spaces after REG_SZ is of real interest, but could contain also a space character. So I used in batch code below not tokens=4, but instead tokens=3* and ignored %b which holds REG_SZ. Instead %c is assigned to environment variable var resulting in getting really entire string value even if the string contains 1 or more spaces.
And the environment variable var is deleted before a new query on next computer is executed in case of a computer does not contain the registry value at all. The error message written by reg.exe to stderr is redirected to device nul for this case. The value of var would be unchanged from previous computer if not deleted before running the next query.
#echo off
setlocal EnableDelayedExpansion
for /F %%a in (C:\batchFiles\computers.txt) do (
set var=
for /F "skip=4 tokens=3*" %%b in ('%SystemRoot%\System32\reg.exe query "\\%%a\HKLM\SOFTWARE\some\any" /v "Forms Path" 2^>nul') do set "var=%%c"
if "!var!" == "\\server\folder\forms\path" (
echo %%a has correct value.
) else if "!var!" == "" (
echo %%a does not have the value at all.
) else (
echo %%a has wrong value.
)
pause
)
endlocal
I am not a scripting guru nor do I play one in real life.
I am trying to do the following on a Windows 2003 server (yes Win2K3..dont ask..please)
I need to check the number of files in D:\mailqueue
If the # of files is zero then do nothing.
If the # of files is more than 1 then restart the windows SMTP service.
Once the restart is done, check the same folder again to make sure file count is zero.
If not then execute SMTP restart command again.
Keep restarting till queue is zero.
HELP!!!!
I think i got the first part figured out, the count of # of files (yes, may seem trivial but its a HUGE achievement for me)...
#echo off
setlocal
set /a count=0
for /F %%N in ('dir "C:\mailqueue" ^| find "File(s)"') do set count=%%N
echo count=%count%
endlocal
Now I need to know how to take this value in count and if it is greater than zero then execute a restart of a windows service. After the restart I need to do a count again and see if its zero. if its not then do the restart again and keep doing it till count is zero.
muchos gracias
This should work for you-
:do_it_again
for /f %%i in ('dir c:\mailqueue ^| findstr /i /c:"file(s)"') do set count=%%i
if "count" neq "1" (
net stop smtpsvc & net start smtpsvc
)
goto :do_it_again
Cheers, G