I'm a cmd.exe master
Par Olivier Mengué le dimanche 28 janvier 2007, 01:52 - Code - Lien permanent
A few months ago, Paul Bissex posted this on his blog: Let’s play a game: BASIC vs. Ruby vs. Python vs. PHP. Just a small exercise at implementing a simple algorithmic problem in diverse programming languages.
I replied with an implementation in an old and widespread but still much unknown, language: the Microsoft Command Processor (cmd.exe
). Old because it inherits from the command.com present in 80's {PC,MS}-DOS. Widespread because it is available on every Windows machine in the world. Unknown because few use it.
Some will say that it deserves it. Since the DOS times, Microsoft added various extensions to what was known as "batch files" to gives us improved loops, conditionnals, and gosubs. But the language is still far from any Unix shell as escaping special chars is often difficult, sometimes impossible.
So, implementing Reverse for Cmd.exe was a challenge. But it was also for me a look in the past. I started to learn to write batch files even before having a PC at home. My first batch files were written on paper, while reading the MS-DOS 5.0 manual that the seller of the PC gave me one month before the PC itself. Batch files on MS-DOS has been my first experience at builing tool for system administration at a time where most of my friends were just using their PCs only for games. You can still find some sample of those old scripts on my old web site. My biggest achievement (in lines of code at least) is probably "Dolmen's config", a set of boot scripts (config.sys
, autoexec.bat
) optimized for launching about 100 games. The lack of power of batch files is one of the reasons that pushed me to learn and practice x86 assembly language first with Microsoft's debug.exe
(that you also still have on your Windows PC).
Here is the code implemented in pure cmd.exe (no external program called). You can test it on any Microsoft Windows 2000/XP (probably Vista too) without having to install any virtual machine or interpreter. Just save it as reverse.cmd
and run it from the command prompt. I had to use many tricks I learned during the past years. String operations can be very hard to implement and are not perfect due to the lack of character escaping.
@echo off verify other 2>nul setlocal enabledelayedexpansion if errorlevel 1 echo Windows 2000/XP required.& goto :EOF set sorted=%2 set sorted=%sorted:"=% if "%sorted%"=="" ( set sorted=123456789 rem set sorted=ABCDE ) set current=%1 set current=%current:"=% if "%current%"=="" ( set current=%sorted% call :Shuffle current ) set steps=0 :Play echo.%current% if "%current%"=="%sorted%" goto Done set /P flipcount=Reverse how many? if /I "%flipcount:~0,1%"=="q" goto :EOF set left=!current:~0,%flipcount%! set right=!current:~%flipcount%! call :Reverse left set current=%left%%right% set /A steps+=1 goto Play :Done :: The only way to print '!' is to disable DelayedExpansion. :: Yes, cmd.exe is really flawed: there is no escape char for '!' in :: DelayedExpansion mode. setlocal disabledelayedexpansion echo Done! That took you %steps% steps. endlocal goto :EOF :: ======= FUNCTIONS ======= :: Compute number of characters of variable named %1 :: Result is returned in both %errorlevel% and %Length% :Length setlocal enabledelayedexpansion set l=0 :LengthLoop set _=!%1:~%l%,1! if not "%_%"=="" set /A l+=1& goto LengthLoop endlocal & set Length=%l% :: Set errorlevel and return exit /B %Length% :: Reverse characters of variable named %1 :Reverse setlocal enabledelayedexpansion call :Length %1 set /A p=Length/2 set r=!%1:~%p%,-%p%! :ReverseLoop set /A q=p-1 set r=!%1:~-%p%,1!%r%!%1:~%q%,1! set p=%q% if not %p%==0 goto ReverseLoop endlocal & set %1=%r% goto :EOF :: Shuffle characters in variable named %1 :Shuffle setlocal enabledelayedexpansion call :Length %1 set /A count=5*Length set d=!%1! for /L %%i in (1,1,%count%) do call :ShuffleSub d endlocal & set %1=%d% goto :EOF :ShuffleSub set /A n=^(Length * %RANDOM%^) / 32768 set right=!%1:~%n%! set %1=!right:~1!!%1:~%n%,1!!%1:~0,%n%! ::echo.!%1! goto :EOF
Update 2007-02-22: added syntax highlighting.