One of my tasks at an old job was server maintenance and monitoring. Quite often, a lot of tasks that should be done on a regular basis are just simply overlooked because they’re not the sort of things that most people think about. For example, de-fragmenting a hard drive often doesn’t happen until the drive is so fragmented the performance is noticeably affected and people start to complain about the speed of opening and saving their files, or even just logging in to their system.

Windows 7 and Windows Server 2008 have resolved this significantly with their new scheduler subsystem. You can now easily set the server or host to perform many tasks very easily. In fact, de-fragmenting and backing up have become to such tasks that you can easily schedule to happen overnight. But on older versions it required a little more effort.

One of the issues that I have always had with systems like Windows Task Scheduler when compared with crond or atd from the Unix/Linux world is that there is not really any easy way to get feedback or output from your tasks. Unless the application you’re running has the ability to log its results to your Application event logs, often the only thing you will ever learn is the result code or error level generated when the application closed.

Coming from a Linux background myself, one of the ways I would get around this is to create batch or cmd scripts that would generate logs with detailed or useful information in them. Instead of running defrag directly on a Windows server, I would run the script instead. The output would get stored in a text file that I could then read at a later time. To enable a historical record, I would name the files with that days date in them. This means that tomorrows scheduled event won’t overwrite todays logs.

And thus we finally come to the point of this tutorial. Actually determining the date, formatting it and then using it in the new filename of the log we are creating in a dos batch script or a command shell cmd script.

date -t
When we use the %DATE% environment variable in our shell scripts, it returns the same information as running “date /t” at the command prompt. The format is generally based on your language settings in Windows. So for those of us in New Zealand, its most likely going to be “DDD dd/mm/yyyy” or for those of you in the US it might likely be “DDD mm/dd/yyyy” or something different again. You can change this format in your language settings in the Control Panel of Windows.

Problem that we have here is that I prefer the date in my log files to go in the reverse order. I generally use the “yyyymmdd” format for log files. This makes grouping very easy. It also means that the last digits roll up every day and thus acts like a normal counter in that regard. You can have many years of logs and be able to quickly and easily get through them.

date dos command
But to get that format from the %DATE% variable requires a bit of work. We basically need to strip the variable down into blocks. We know the format it uses, and therefore we know exactly which characters represent the numbers we need. So we can use multiple copies of substrings of the variable to get what we need.

To do this we need to use the substring delimiter %VARIABLE:~#,#%

echo %date%
echo %date:~0,3%

Broken down, we say skip no characters, start at the beginning and print the next 3 characters. In my case, this was “Sun” for Sunday. Now lets strip Sun out, but keep the rest of the date.

date image
We know how many characters make up the string. “DDD dd/mm/yyyy” is 14 characters. We know that the dd/mm/yyyy is the last 10 characters. So we want to skip the first 4 characters of the date string “DDD “ and print only the 10 characters that make up the date.

echo %date%
echo %date:~4,10%

We get a very simple out put that we can now start to make real use of. But I want the name of my log files to have the date format “yyyymmdd” not “dd/mm/yyyy”. This is where things start to get a little more messy. We are going to have to break the string up multiple times and then put it back together as a single string. This is still a single command line, but its multiple commands chained together to make just one.

We know that the day is a 2 digit number starting at the 5th character, so we need to skip the first 4 characters.
We know that the month is a 2 digit number starting at the 8th character, so we need to skip the first 7 character.
We know that the year is a 4 digit number starting at the 11th character, so we need to skip the first 10 characters.

The result is something that looks like the following.

echo %date%
echo %date:~10,4%%date:~7,2%%date:~4,2%

date dos command
Great, now we have our date into the “yyyymmdd” format we can use for names of folders or files when creating a logs.

So lets create a script that will allow us to run a defrag, capture the output to a text file and use the date as part of the filename to make managing them a lot easier.

@ECHO OFF
GOTO START

Simple Defrag Script by Stephen Cropp of stevesgeekspeak.com
Copyright (C) 2010 by Stephen Cropp

This script is released under the terms of the BSD license.
You can find a summary and text of the license terms at the following website

http://creativecommons.org/licenses/BSD/

THIS SOFTWARE IS PROVIDED BY STEPHEN CROPP "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

:START

REM First lets break up the dates into each variables to make reading simpler.
SET YEAR=%DATE:~10,4%
SET MONTH=%DATE:~7,2%
SET DAY=%DATE:~4,2%

REM The directory for our logs with year and month subdirectories for easy management.
SET LOGDIR=C:\Logs\%YEAR%\%MONTH%

REM The date in the format we want. We could rearrange this as we please
SET TODAY=%YEAR%%MONTH%%DAY%

REM The name of our log file
SET LOGFILE=%LOGDIR%\defrag-%TODAY%.txt

REM If the directory for our lots does not exist, create it and make it our
REM working directory. POPD and PUSHD could be useful here too if it were
REM a more complex script.

IF NOT EXIST %LOGDIR% MKDIR %LOGDIR%
CHDIR %LOGDIR%

REM Put a date header in our logfile that includes the time and date started.
REM Include a unique separator so we can easily search if its run multiple
REM times a day.

ECHO ---------- >> %LOGFILE%
ECHO *** Defrag started %DATE:~4,10% at %TIME:~0,5% >> %LOGFILE%
ECHO ---------- >> %LOGFILE%

REM Note, check your version of defrag.exe's help for command switches.
REM The example shown will work on Windows 7, but not on Windows XP.
REM At a command prompt, type "defrag /?" to get the switches.

REM Run a defrag and append the output to our logfile
REM We then log the results of the defrag ERRORLEVEL to our file

%WINDIR%\System32\Defrag.exe /C /V /X >> %LOGFILE%
IF ERRORLEVEL 7 ECHO *** Exit Notice: Low disk space, under 15% free on current volume >> %LOGFILE%
IF ERRORLEVEL 6 ECHO *** Exit Notice: System, ACL, file mismatch, system wide error >> %LOGFILE%
IF ERRORLEVEL 5 ECHO *** Exit Notice: General error >> %LOGFILE%
IF ERRORLEVEL 4 ECHO *** Exit Notice: Low memory condition (RAM) >> %LOGFILE%
IF ERRORLEVEL 3 ECHO *** Exit Notice: Unforeseen, unknown error >> %LOGFILE%
IF ERRORLEVEL 2 ECHO *** Exit Notice: Bad parameter, syntax error >> %LOGFILE%
IF ERRORLEVEL 1 ECHO *** Exit Notice: User aborted, cancelled >> %LOGFILE%
IF ERRORLEVEL 0 ECHO *** Exit Notice: Completed, no errors >> %LOGFILE%

REM Include a finish time so we can easily see how long it takes.
ECHO ---------- >> %LOGFILE%
ECHO *** Defrag finished %DATE:~4,10% at %TIME:~0,5% >> %LOGFILE%
ECHO ---------- >> %LOGFILE%

REM Unset the variables we created, just because its good practice.
UNSET LOGDIR
UNSET TODAY
UNSET LOGFILE
UNSET YEAR
UNSET MONTH
UNSET DAY

REM GAME OVER. All done.

I have commented the script so you should be able to follow it relatively easily. If there is something there that doesn’t make sense to you, please let me know in the comments.

Copy the text of that script to a text file and give it a useful name like “scheddefrag.bat” or similar. Save it in a folder you can easily get to. I personally save lots of those kinds of scripts in C:\Utils\ just because it makes life a lot easier when working at the command prompt. I also add that directory to the Systems %PATH% environment variable (as opposed to the user’s) so that I can easily run my scripts no matter where I am in the directory structure of my machine.

The next step to do after you’ve saved that batch script somewhere is to add it to your scheduled tasks to run at a time that suits you. I’ll leave this up to you to work out for now.

Lets say our directory was full of those log files. We’d been working with them for a few months and had a couple of hundred text files in there. We want to see just todays text file.

Now if, for example, you wanted to get a list of files created today in a given directory, you could combine that with the forfiles command. Something like…

@echo off
set newerthan=%date:~4,10%
forfiles /p C:\Logs\ /m*.* /D+%newerthan% /c "cmd /c echo @file"

This will give us a list of all files created today in the directory C:\Logs\.

The environment variable %newerthan% contains todays date in dd/mm/yyyy format. Because the server can’t create files newer than the current date, it will only show todays files.

You could take this a step further and use simple math to get the date of a day a week earlier and use that in the %newerthan% variable. There are lots of possibilities. In that example I just list the file names. But you could also use the forfiles command to list files older than a certain date and delete them. Thus cleaning things up further.

You can do lots of things with environment variables built into the system, but quite often being able to get just a part of that variable is very useful. Hopefully this tutorial will help you experiment further with other possible variables you might discover.