Thursday, December 06, 2012

Elapsed time in a Windows Shell script

That is easy to do in bash (just add time in front of the command you want to time), and it is also possible to do it in DOS. It just took me about 70 lines of script ;0) Here is an example:
 @setlocal
 @echo off
 set begintime=%time%
 @echo -----------------------------------------
 @echo Starting: %begintime% ...
 set /p var=Tell me something and hit [return] ^> 
 @echo -----------------------------------------
 set finishtime=%time%
 call :timediff %begintime% %finishtime%
 ::@echo.
 @echo Done at %finishtime%, it took you %timetaken% sec to tell me [%var%].
 @echo -----------------------------------------
 goto eos
 ::
 :timediff
 :: echo %1, %2
 set starttime=%1
 set endtime=%2
 if [%starttime:~1,1%] == [:] set starttime=0%starttime%
 if [%endtime:~1,1%] == [:] set endtime=0%endtime%
 set startcsec=%starttime:~9,2%
 set startsecs=%starttime:~6,2%
 set startmins=%starttime:~3,2%
 set starthour=%starttime:~0,2%
 set /a starttime=(%starthour%*60*60*100)+(%startmins%*60*100)+(%startsecs%*100)+(%startcsec%) 
 ::
 set endcsec=%endtime:~9,2%
 set endsecs=%endtime:~6,2%
 set endmins=%endtime:~3,2%
 set endhour=%endtime:~0,2%
 if %endhour% LSS %starthour% set /a endhour+=24
 set /a endtime=(%endhour%*60*60*100)+(%endmins%*60*100)+(%endsecs%*100)+(%endcsec%)
 set /a timetaken= ( %endtime% - %starttime% )
 set /a timetakens= %timetaken% / 100
 set timetaken=%timetakens%.%timetaken:~-2%
 goto eos
 :: End Of Script
 :eos
 @endlocal
Notice the code under the timediff label, this is where the skill is. It takes the time before and the time after as parameters, and computes the value of a timetaken variable that you can display.
It is all based on the value of the %time% variable:
 Prompt> echo %time%
 10:44:29.25
and also on the substring manipulation, performed with the ":~" characters.
It will generate an output like this one:
 -----------------------------------------
 Starting: 10:40:02.63 ...
 Tell me something and hit [return] > Whatever
 -----------------------------------------
 Done at 10:40:11.17, it took you 8.54 sec to tell me [Whatever].
 -----------------------------------------
There is a gotcha, though. A problem remains, when you write something like this:
 set /a num=(010 + 010)
Windows comes up with
 16
It is because it considers that 010 is in octal, as such, understood as 8 (decimal). Whatever begins with a '0' is an octal digit, so '08', '09', etc, are invalid numbers.
In the calculation above, you need to remove the leading '0' for the computation to be done in decimal...
Being aware of this, the script becomes (its timediff part, and on):
 :timediff
 :: echo from [%1] to [%2]
 set starttime=%1
 set endtime=%2
 if [%starttime:~1,1%] == [:] set starttime=0%starttime%
 if [%endtime:~1,1%] == [:] set endtime=0%endtime%
 set startcsec=%starttime:~9,2%
 set startsecs=%starttime:~6,2%
 set startmins=%starttime:~3,2%
 set starthour=%starttime:~0,2%
 :: Remove leading 0 (considered as octal numbers)
 call :removeLeadingZero %startcsec%
 set startcsec=%truncated%
 call :removeLeadingZero %startsecs%
 set startsecs=%truncated%
 call :removeLeadingZero %startmins%
 set startmins=%truncated%
 call :removeLeadingZero %starthour%
 set starthour=%truncated%
 ::
 set /a starttime=(%starthour%*60*60*100)+(%startmins%*60*100)+(%startsecs%*100)+(%startcsec%) 
 ::
 set endcsec=%endtime:~9,2%
 set endsecs=%endtime:~6,2%
 set endmins=%endtime:~3,2%
 set endhour=%endtime:~0,2%
 :: Remove leading 0 (considered as octal numbers)
 call :removeLeadingZero %endcsec% 
 set endcsec=%truncated%
 call :removeLeadingZero %endsecs%
 set endsecs=%truncated%
 call :removeLeadingZero %endmins%
 set endmins=%truncated%
 call :removeLeadingZero %endhour%
 set endhour=%truncated%
 ::
 if %endhour% LSS %starthour% set /a endhour+=24
 set /a endtime=(%endhour%*60*60*100)+(%endmins%*60*100)+(%endsecs%*100)+(%endcsec%)
 set /a timetaken= ( %endtime% - %starttime% )
 set /a timetakens= %timetaken% / 100
 set timetaken=%timetakens%.%timetaken:~-2%
 goto eos
 ::
 :removeLeadingZero
 set truncated=%1
 :top
 if not [%truncated:~1%] == [] (
   if [%truncated:~0,1%] == [0] (
     set truncated=%truncated:~1%
     goto top
   )
 )
 :: if not [%1] == [%truncated%] @echo [%1] becomes [%truncated%]
 goto eos
 :: End Of Script
 :eos
 @endlocal
Notice the new sub-routine removeLeadingZero. It leaves at least one character in the string to truncate ('0' is the same in octal and decimal). The statement if not [%truncated:~1%] == [] (... means "if the length of %truncated% is greater than 1". The statement
 if [%starttime:~1,1%] == [:] set starttime=0%starttime%
is here to manage the times before 10:00. It's returned like
  9:55:10.34
That makes the first ':' in position 2, instead of 3, as expected. This last line in transformed so it's saying
  09:55:10.34
This way, all is good!
Now, if you don't want to copy-paste this code every time you want to time a command, start the script this way:
 @setlocal
 @echo off
 set begintime=%time%
 @echo -----------------------------------------
 @echo Starting: %begintime% ...
 @echo -----------------------------------------
 :: Execution to time goes here
 %*
 ::
 @echo -----------------------------------------
 set finishtime=%time%
 call :timediff %begintime% %finishtime%
 ::@echo.
 @echo Done at %finishtime%, execution of [%*] took %timetaken% sec.
 @echo -----------------------------------------
 goto eos
 ::
 :timediff
 :: echo from [%1] to [%2]
   ...
Name it something like "elapsed.cmd", and run it this way:
 Prompt> elapsed java -version
 -----------------------------------------
 Starting:  9:24:08.48 ...
 -----------------------------------------
 java version "1.6.0_20"
 Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
 Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01, mixed mode)
 -----------------------------------------
 Done at  9:24:08.63, execution of [java -version] took 0.15 sec.
 -----------------------------------------
And that's it. The full script is 74 lines long, including comments. Here is the full script:
@setlocal
@echo off
@echo Usage:
@echo   %0 ^<command ^<prm1 ^<prm2 ^<...^>^>^>
set begintime=%time%
@echo -----------------------------------------
@echo Starting: %begintime% ...
@echo -----------------------------------------
@echo %CD%^> %*
:: Execution to time goes here
%*
::
@echo -----------------------------------------
set finishtime=%time%
call :timediff %begintime% %finishtime%
::@echo.
@echo Done at %finishtime%, execution of [%*] took %timetaken% sec.
@echo -----------------------------------------
goto eos
::
:timediff
:: echo from [%1] to [%2]
set starttime=%1
set endtime=%2
if [%starttime:~1,1%] == [:] set starttime=0%starttime%
if [%endtime:~1,1%] == [:] set endtime=0%endtime%
set startcsec=%starttime:~9,2%
set startsecs=%starttime:~6,2%
set startmins=%starttime:~3,2%
set starthour=%starttime:~0,2%
:: Remove leading 0 (considered as octal numbers)
call :removeLeadingZero %startcsec%
set startcsec=%truncated%
call :removeLeadingZero %startsecs%
set startsecs=%truncated%
call :removeLeadingZero %startmins%
set startmins=%truncated%
call :removeLeadingZero %starthour%
set starthour=%truncated%
::
set /a starttime=(%starthour%*60*60*100)+(%startmins%*60*100)+(%startsecs%*100)+(%startcsec%)
::
set endcsec=%endtime:~9,2%
set endsecs=%endtime:~6,2%
set endmins=%endtime:~3,2%
set endhour=%endtime:~0,2%
:: Remove leading 0 (considered as octal numbers)
call :removeLeadingZero %endcsec%
set endcsec=%truncated%
call :removeLeadingZero %endsecs%
set endsecs=%truncated%
call :removeLeadingZero %endmins%
set endmins=%truncated%
call :removeLeadingZero %endhour%
set endhour=%truncated%
::
if %endhour% LSS %starthour% set /a endhour+=24
set /a endtime=(%endhour%*60*60*100)+(%endmins%*60*100)+(%endsecs%*100)+(%endcsec%)
set /a timetaken= ( %endtime% - %starttime% )
set /a timetakens= %timetaken% / 100
set timetaken=%timetakens%.%timetaken:~-2%
goto eos
::
:removeLeadingZero
set truncated=%1
:top
if not [%truncated:~1%] == [] (
  if [%truncated:~0,1%] == [0] (
    set truncated=%truncated:~1%
    goto top
  )
)
:: if not [%1] == [%truncated%] @echo [%1] becomes [%truncated%]
goto eos
:: End Of Script
:eos
@endlocal
    

Saturday, December 01, 2012

Technology vs Skills & Intelligence

Maleskine vs Harrison.
The full story can be read in Dava Sobel's book, "Longitude", about this contest set by the British Admiralty, after a substantial number of war ships were lost on their way back from a naval battle (1707), where they had defeated the French navy.
Those ships were lost, mainly because of the lack of mean to evaluate the longitude, they wrecked on the Scilly Islands, thinking they were a couple of miles more east. Everyone already knew that the knowledge of the longitude relied on the exact knowledge of the time. Four seconds of time mean one second of longitude, and one second of arc on a meridian is - by definition - a nautical mile.

Another side effect of this lack of means to correctly estimate the longitude was the following one:
On a trip from east to west, for example from Europe to the West Indies, in order not to miss their destination, the ships used to reach as soon as possible the latitude of the port they were targeting. Once the right latitude was reached, they kept sailing due west. This way, even if they could not tell exactly what their longitude was, whenever they would make landfall, it would be close to their destination. And here is the trick. Knowing more or less where those ships were going, pirates and privateers just had to wait for them somewhere along the way, those big clumsy ships were for them like low-hanging fruits, just waiting to be picked-up... The safe route was risky because of the pirates, another one was risky because it was unknown and unsafe...
(About pirates and others villains, do check out the remarkable work of Marcus Rediker).

In her book, Dava Sobel seems to prefer John Harrison to Neville Maleskine - and I will not argue with that, but beside the political aspect of that situation, something interesting shows up, like a watermark..., this is not the first thing you see, but it becomes obvious after a while, as a first class part of the whole picture.

John Harrison came up with several very elaborated clocks and watches, real pieces of art, still visible in the Greenwich Royal Observatory. His idea was to build the most accurate time keeping piece.

The approach Neville Maleskine took was drastically different. To him, the accuracy of such a device was out of reach. He came up with a method to know by how much your time keeper is off. It relies on the moon position in the sky. As a matter of fact, the moon has a very special behavior. All celestial bodies have an apparent movement going from east to west. The sun, the planets, the stars, and the moon roam the sky from east to west. The moon though, behaves in a unique manner, the day after, it's at a location it would have had earlier the day before. From one day to the next, the moon moves backwards, west to east. This is what Maleskine used. He considered the sky as a clock-face, where the numbers are the stars, and the hand is the moon. He relied on a celestial clock, not a man-made one. Then by measuring the distance between a celestial body (as close as possible to the ecliptic) and the moon, you could calculate what "Celestial Time" it is.
On top of that, the observation of this distance (such a distance is an angle, measured with a sextant) does not depend on the position of the observer on the Earth.

Both Harrison's and Maleskine's methods do work.
Harrison's approach is much faster. You read the clock, then you know what time it is, period. The calculations involved in Maleskine's method are huge, and as a result, time consuming, and an error might very well sneak in.

But let's take a step backward.
Maleskine's method requires the observer to have solid mathematical background. You need to be fluent in trigonometry to at least apprehend the rationale. You also need to rely on some almanac, which you take at sea with you. If you are OK with that, you're all set, and like riding a bike, this is the kind of skills you'll never loose.

Harrison's path, by far the easiest for the user, happened to have been the most successful up to this day. The time keepers kept improving, we now have GPSs, atomic clocks, all kinds of instruments, all available at sea. Maleskine's tables stopped to be published in 1911, when radio beeps begun to be emitted for any ship equipped with a radio to have the accurate time.
Celestial Navigation is no longer taught in most naval academies. Now, to know where you are (not even to know what time it is), you push a button, or read a digital screen.

In short, Maleskine's method requires the user to be aware of the elaboration of the rationale, in its every smallest details.
Harrison's one just asks the user to be able to read a display; the machine behind the scene takes care of everything else.

An interesting point of view to mention: What if it fails?
If Maleskine's method fails, you can try to recalculate, this is probably where the problem comes from.
If Harrison's method fails, you must be able to repair the clock...


I'm not 100% comfortable with this idea of relying on a machine I did not build myself (and same for the ones I built). I very well know that those machines can shut down in no time, with no warning. This is a systemic feature of whatever we call "machine".

Remember the first Gulf War? The US Army - who owned the GPS satellites - decided to shut down the public access to their data. Just like that, boom. Some times, even the army can be snappy...

Whoever was at sea, using a GPS did indeed notice.
Whoever was at sea, using another method, kept sailing without noticing...

Navigation aids should bear a label saying something like "Use responsibly". Always have a backup plan.

Self sufficiency, freedom, all this takes time, energy, effort; it is not always easy, nor relaxing, it is always demanding. Freedom is no vacation. But that is so rewarding when it works! So much more rewarding than owning the last cutting edge piece of technology, iStuff or what not.

I'm not spitting in the soup. I'm just trying to identify what I really need, to reach my goals.
And what I don't.