Why is the array filled with "m" - windows

I'm fairly new to batch and I've been trying to write some simple sorting programs. This program uses the most basic sorting system, and the code(from what I can see) seems to be error-free. Yet when I run it, a random list is generated, and it seems as if some sorting is going on, then the array is filled with the letter "m". I don't see why this is occurring, so if somebody could point me in the correct direction I would greatly appreciate it.
My code:
#echo off
color b
title sorting
set ar=0
set num=0
set check=0
set checknum=0
set totalnumber=500
set randmax=5000
:array
if %num% LSS %totalnumber% (
set /A a[%num%]=%random% %% %randmax%
set /A num=%num%+1
goto array
)
if %num% EQU %totalnumber% (
goto echo1
)
:echo1
for /F "tokens=2 delims==" %%s in ('set a[') do echo %%s
echo sort initialized
goto sort
)
:sort
set n=0
:sortloop
set /A m=%n%+1
if %n% EQU %totalnumber% (
goto check
)
if %a[%n%]% GTR %a[%m%]% (
set hold=%a[%m%]%
set a[%m%]=%a[%n%]%
set a[%n%]=%hold%
set /A n=%n%+1
goto sortloop
)
if %a[%n%]% LSS %a[%m%]% (
echo a[%n%] check
set /A n=%n%+1
goto sortloop
)
:check
set check=0
set checknum=0
:checkloop
set /A checknumplus=%checknum%+1
if %check% EQU %totalnumber% (
goto complete
)
if %checknum% EQU %totalnumber% (
set n=0
goto sort
)
if %a[%checknum%]% LSS %a[%checknumplus%]% (
set /A check=%check%+1
set /A checknum=%checknum%+1
goto checkloop
)
:complete
for /F "tokens=2 delims==" %%s in ('set a[') do echo %%s
for /F "tokens=2 delims==" %%s in ('set a[') do echo %%s > sortedlist.txt

When you need to use variables inside of variables in batch (most commonly when working with arrays), you need to use delayed expansion.
Right now, your code says set hold=%a[%m%]%. The interpreter is treating this value as the variable %a[% (which doesn't exist, so it using nothing), the literal character m, and the variable %]% (which also doesn't exist and is therefore empty).
To get around this, put setlocal enabledelayedexpansion at the top of your code and then change your set statement to set hold=!a[%m%]! (and do the same thing with the other lines that are using it).

Related

Coding two real time progress bars in batch

I really like the progress bar that #MCND coded in this post - Coding a real time progress bar in batch
Is there any way to add a second real-time progress bar just below the first one which could be manipulated seperately showing the overall progress of the batch file, while the first progress bar shows the real-time progress of the current function?
I tried copying the :drawProgressBar, :initProgressBar and :finalizeProgressBar subroutines and adding a 1 to the end of them and all pb variables in the subroutines to create a distinct subroutine, but no luck.
#MCND 's original post/code:
Just a skelleton. Adapt as needed.
The basic idea is to output the line with the progress bar with an ending carriage return to return to the start of the line and be able to repaint the next state over the previous one.
All "problematic" code wrapped into subroutines so you only need to call :drawProgressBar percentValue "operationText"
#echo off
setlocal enableextensions disabledelayedexpansion
for /l %%f in (0 1 100) do (
call :drawProgressBar %%f "up test with a long text that will not fit on screen unless you have a lot of space"
)
for /l %%f in (100 -1 0) do (
call :drawProgressBar %%f "going down test"
)
for /l %%f in (0 5 100) do (
call :drawProgressBar !random! "random test"
)
rem Clean all after use
call :finalizeProgressBar 1
call :initProgressBar "|" " "
call :drawProgressBar 0 "this is a custom progress bar"
for /l %%f in (0 1 100) do (
call :drawProgressBar %%f
)
endlocal
exit /b
:drawProgressBar value [text]
if "%~1"=="" goto :eof
if not defined pb.barArea call :initProgressBar
setlocal enableextensions enabledelayedexpansion
set /a "pb.value=%~1 %% 101", "pb.filled=pb.value*pb.barArea/100", "pb.dotted=pb.barArea-pb.filled", "pb.pct=1000+pb.value"
set "pb.pct=%pb.pct:~-3%"
if "%~2"=="" ( set "pb.text=" ) else (
set "pb.text=%~2%pb.back%"
set "pb.text=!pb.text:~0,%pb.textArea%!"
)
<nul set /p "pb.prompt=[!pb.fill:~0,%pb.filled%!!pb.dots:~0,%pb.dotted%!][ %pb.pct% ] %pb.text%!pb.cr!"
endlocal
goto :eof
:initProgressBar [fillChar] [dotChar]
if defined pb.cr call :finalizeProgressBar
for /f %%a in ('copy "%~f0" nul /z') do set "pb.cr=%%a"
if "%~1"=="" ( set "pb.fillChar=#" ) else ( set "pb.fillChar=%~1" )
if "%~2"=="" ( set "pb.dotChar=." ) else ( set "pb.dotChar=%~2" )
set "pb.console.columns="
for /f "tokens=2 skip=4" %%f in ('mode con') do if not defined pb.console.columns set "pb.console.columns=%%f"
set /a "pb.barArea=pb.console.columns/2-2", "pb.textArea=pb.barArea-9"
set "pb.fill="
setlocal enableextensions enabledelayedexpansion
for /l %%p in (1 1 %pb.barArea%) do set "pb.fill=!pb.fill!%pb.fillChar%"
set "pb.fill=!pb.fill:~0,%pb.barArea%!"
set "pb.dots=!pb.fill:%pb.fillChar%=%pb.dotChar%!"
set "pb.back=!pb.fill:~0,%pb.textArea%!
set "pb.back=!pb.back:%pb.fillChar%= !"
endlocal & set "pb.fill=%pb.fill%" & set "pb.dots=%pb.dots%" & set "pb.back=%pb.back%"
goto :eof
:finalizeProgressBar [erase]
if defined pb.cr (
if not "%~1"=="" (
setlocal enabledelayedexpansion
set "pb.back="
for /l %%p in (1 1 %pb.console.columns%) do set "pb.back=!pb.back! "
<nul set /p "pb.prompt=!pb.cr!!pb.back:~1!!pb.cr!"
endlocal
)
)
for /f "tokens=1 delims==" %%v in ('set pb.') do set "%%v="
goto :eof

Find the biggest of 4 numbers in Windows Batch scripting

I am trying to find the biggest of 4 numbers in batch scripting but it is not working.
The GTR command is not getting executed.
From this line it never gets executed if !Counter_Senior! gtr !Max_Age! (
I'm new to batch scripting, I am not sure about alignment and spacing. please help me through.
echo off
setlocal enabledelayedexpansion
set /a Counter_Child=1
set /a Counter_Senior=2
set /a Counter_Older_adult=0
set /a Counter_Young_adult=3
set /a Max_Age=%counter_Child%
echo maximum age is %Max_Age%
if !Counter_Senior! gtr !Max_Age! (
set Max_Age=%Counter_Senior%
if !Counter_Older_adult! gtr !Max_Age! (
set Max_Age=%Counter_Older_adult%
if !%Counter_Young_adult! gtr !Max_Age! (
set Max_Age=%Counter_Young_adult%
time /t
echo Maximum age is %Max_Age%
goto:EOF
)
)
)
if !Counter_Older_adult! gtr !Max_Age! (
set %Max_Age%=%Counter_Older_adult%
if !%Counter_Young_adult! gtr !Max_Age! (
set %Max_Age%=%Counter_Young_adult%
echo Maximum age is %Max_Age%
goto:EOF
)
)
if !%Counter_Young_adult! gtr !Max_Age! (
set %Max_Age%=%Counter_Young_adult%
echo Maximum age is %Max_Age%
goto:EOF
)
echo Maximum age is %Max_Age%
goto:EOF
Your code seems way too complicated. Try this:
echo off
set Counter_Child=1
set Counter_Senior=2
set Counter_Older_adult=0
set Counter_Young_adult=3
set Max_Age=%Counter_Child%
IF %Counter_Senior% GTR %Max_Age% SET Max_Age=%Counter_Senior%
IF %Counter_Older_adult% GTR %Max_Age% SET Max_Age=%Counter_Older_adult%
IF %Counter_Young_adult% GTR %Max_Age% SET Max_Age=%Counter_Young_adult%
echo maximum age is %Max_Age%
If you only want the maximum value of the four Counter variables then you could use this:
For /F "Tokens=2 Delims==" %%A In ('Set Counter_'
) Do If %%A GEq !Max_Age! Set/A Max_Age=%%A
[Edit]And for the scenario suggested by Anders…
For %%A In (
%Counter_Child% %Counter_Senior% %Counter_Older_adult% %Counter_Young_adult%
) Do If %%A GEq !Max_Age! Set/A Max_Age=%%A
There are (at least) two problems with your code:
You are using goto:EOF inside a if and that quits the entire batch file.
In some places you incorrectly used set %Max_Age%=.. instead of set Max_Age=..
Your code is also overcomplicated and can be reduced to something simpler like the answer posted by MichaelS.
If you want to print specific information when a change is made you can use a generic sub procedure:
#echo off
goto Start
:CheckAge
setlocal enableextensions enabledelayedexpansion
set newage=!%1!
if %newage% gtr %Max_Age% (
echo %1 is older than %Max_Age%, the new maximum is %newage%
set Max_Age=%newage%
)
endlocal & set Max_Age=%Max_Age%
goto :EOF
:Start
set Counter_Child=1
set Counter_Senior=2
set Counter_Older_adult=0
set Counter_Young_adult=3
set Max_Age=0
call :CheckAge Counter_Child
call :CheckAge Counter_Senior
call :CheckAge Counter_Older_adult
call :CheckAge Counter_Young_adult
echo Maximum age is %Max_Age%

How to sort file names numerically in windows command line?

The windows explorer does display in correct order. For example: filename1.txt, filename2.txt, filename11.txt etc. When I tried the DIR command with different options could not get the same behaviour. It displays filename1.txt, filename11.txt, filename2.txt.
Is there any way to sort it so that numeric ordering is preserved?
#ECHO Off
:: dir list in numeric-value order
SETLOCAL
:: %1= directoryname; default current directory
SET "targetdir=%~1"
IF NOT DEFINED targetdir SET "targetdir=.\*.*"
:: remove variables starting $
FOR /F "delims==" %%a In ('set $ 2^>Nul') DO SET "%%a="
SET /a maxnumlength=0
SET "manyzeroes=0000000000000000"
SET "manyzeroes=%manyzeroes%%manyzeroes%%manyzeroes%%manyzeroes%"
:: analyse list of filenames
FOR /f "delims=" %%a IN (
'dir /b /a-d "%targetdir%" '
) DO CALL :detnlen "%%a"
:: build list of filenames
FOR /f "delims=" %%a IN (
'dir /b /a-d "%targetdir%" '
) DO CALL :setname "%%a"
FOR /F "tokens=1*delims=:" %%a In ('set $ 2^>Nul') DO ECHO %%b
GOTO :EOF
:detnlen
SET "name=%~1"
SET /a numlength=0
SET "action=L"
:detnloop
IF %action%==F SET "action=B"
SET "post=%name:~0,1%"&SET "name=%name:~1%"
IF "%post%" geq "0" IF "%post%" leq "9" (
SET /a numlength+=1
SET "action=F"
)
IF %action% neq B IF DEFINED name GOTO detnloop
IF %numlength% gtr %maxnumlength% SET /a maxnumlength=%numlength%
GOTO :EOF
:setname
SET "name=%~1"
SET "pre="
SET "nums="
SET "action=L"
:snloop
IF %action%==F SET "action=B"
SET "post=%name:~0,1%"&SET "name=%name:~1%"
IF "%post%" geq "0" IF "%post%" leq "9" (
SET /a numlength+=1
SET "action=F"
SET "nums=%nums%%post%"
)
IF %action%==L SET "pre=%pre%%post%"&SET "post="
IF %action% neq B IF DEFINED name GOTO snloop ELSE SET "post="
IF %action%==F SET "post="
IF DEFINED nums SET "nums=%manyzeroes%%nums%"
IF DEFINED nums CALL SET "nums=%%nums:~-%maxnumlength%%%"
SET "$%pre%%nums%%post%%name%=:%~1"
GOTO :EOF
Oh, my!
There are no inbuilt switches to show the directory in that odd way, but this has all the hallmarks of a challenge.
OK - it's insanely slow, but it appears to work.
The principle is to determine the longest sequence of initial-numerics as maxnumlength then assign the name of the detected file to an environment variable starting $ and with a name constructed from the filename with the numeric part leading-zero-padded. set $ then lists the $ variables in alphabetic order; select the value, which is the original filename.

BATCH- binary with for

I have to make a script that has to calculate the mask and the net, so I'm trying a script with for but it can not convert the IP to binary. I think I'm not using the variables right.
Any ideas?
#echo off
setlocal enabledelayedexpansion
set var=%1
set /p var=Introduce la ip:
for /F "tokens=1 delims=." %%a in ("%var%") do (
echo %%a
set "vara=%%a"
:binario
set bin=2
set /a resto=%vara%%%bin%
set /a a=%vara%/%bin%
set resultado=%resto%%resultado%
if %vara% GTR 0 (goto binario)
echo %resultado%
goto siguiente
)
:siguiente
for /F "tokens=2 delims=." %%b in ("%var%") do (
echo %%b
)
for /F "tokens=3 delims=." %%c in ("%var%") do (
echo %%c
)
for /F "tokens=4 delims=." %%d in ("%var%") do (
echo %%d
)
goto fin
:vacio
echo Error!
goto fin
:fin
pause
You've got a few minor problems that I see. You set var=%1 but you never check to see whether %1 was supplied before doing set /p var=Enter an IP:. You never call or goto :vacio. As I commented above, modulos within batch scripts need to be written as %% to prevent evaluation as variable chararacters. You don't need % in var names in set /a commands, and you can combine multiple set /a statements with a comma. So instead of
set /a resto=%vara%%%bin%
set /a a=%vara%/%bin%
(which is wrong anyway -- I'll get to that in a minute), I suggest this would be more understandable and maintainable:
set /a resto = vara %% bin, numero = vara / bin
The biggest problem is that you appear to be trying to modify %%a. Don't do that.
If I were you, I would move the decimal to binary conversion to a subroutine, and call it for each octet. Try this:
#echo off
setlocal enabledelayedexpansion
set IP=%1
if "%IP%"=="" set /p "IP=Introduce la ip: "
set idx=0
for %%a in (%IP:.= %) do (
if %%a lss 0 goto vacio
if %%a gtr 255 goto vacio
if !idx! gtr 3 goto vacio
set /P "=%%a = "<NUL
call :dec2bin bin[!idx!] %%a
set /a idx += 1
)
echo %bin[0]%.%bin[1]%.%bin[2]%.%bin[3]%
goto fin
:dec2bin <var_para_definir> <numero>
setlocal enabledelayedexpansion
set numero=%~2
set bin=
for /L %%I in (1,1,8) do (
set /a bit = numero %% 2, numero /= 2
set bin=!bit!!bin!
)
echo %bin%
endlocal & set "%~1=%bin%"
goto :EOF
:vacio
echo Error!
goto fin
:fin
pause
For more information about using call as a function that returns a value, see this page.

Sorting input numbers in batch file

Im trying to create a program in which you will enter 5 numbers randomly and will automatically shows the sorted entered numbers from lowest to highest. Here is my code.
#echo off
:main
set /p num1="Enter 1st :"
set /p num2="Enter 2nd :"
set /p num3="Enter 3rd :"
set /p num4="Enter 4th :"
set /p num5="Enter 5th :"
set /a high=0
set /a low=0
set /a ave=(num1+num2+num3+num4+num5)/5
if %num1% GTR %num2% (set /a high=%num1%) ELSE set /a high =%num2%
if %num3% GTR %high% (set /a high=%num3%)
if %num4% GTR %high% (set /a high=%num4%)
if %num5% GTR %high% (set /a high=%num5%)
if %num1% LSS %num2% (set /a low=%num1%) ELSE set /a low =%num2%
if %num3% LSS %low% (set /a low=%num3%)
if %num4% LSS %low% (set /a low=%num4%)
if %num5% LSS %low% (set /a low=%num5%)
ECHO.
ECHO Average: %ave%
ECHO Highest: %high%
ECHO Lowest: %low%
ECHO Input Numbers: %num1% %num2% %num3% %num4% %num5%
:end
set /p return="Continue?"
if "%return%"=="yes" GOTO main
GOTO exit
:exit
There are several ways to solve this problem. In the "one by one" method each variable must be compared vs. another one; this method is too much complex and is the one you used to read the values. You may also use sort command to sort the numbers (as Rafael tried in his answer); however, the numbers must be padded at left side with zeros in order to be correctly sorted.
When a certain process is repeated over several elements, the usual way to solve such a problem is via an array. You may find descriptions about array concept in several sites, like in Wikipedia. You may read how to use arrays in a Batch file at this post.
The Batch file below make good use of the fact that environment variables are kept automatically sorted; this way, the program just insert the numbers in an array and then take out its elements, so the sort process itself is not required:
#echo off
setlocal EnableDelayedExpansion
set N=5
:main
rem Delete array elements from previous cycle, if any:
for /F "delims==" %%a in ('set array[ 2^>NUL') do set "%%a="
rem Read the numbers; at same time, accumulate they in "ave" variable
rem and create the array with the proper index (with left zeros)
set ave=0
for /L %%i in (1,1,%N%) do (
set /P num="Enter number %%i :"
set /A ave+=num
set index=000000000!num!
set array[!index:~-10!]=!num!
)
set /A ave=ave/N
rem Assemble the output line and get low and high values
set "output="
set "low="
for /F "tokens=2 delims==" %%a in ('set array[') do (
if not defined low set low=%%a
set high=%%a
set output=!output! %%a
)
ECHO/
ECHO Average: %ave%
ECHO Highest: %high%
ECHO Lowest: %low%
ECHO Input Numbers: %output%
set /p return="Continue?"
if "%return%"=="yes" GOTO main
I was looking for an answer to a similar "sort numbers" question, and want to add example of using SORT command as an alternative for completeness. Note that using SORT requires writing a file to disk and seems less efficient:
#echo off
setlocal EnableDelayedExpansion
set "num_1=2" & set "num_2=5" & set "num_3=1" & set "num_4=18" & set "num_5=10"
for /f "tokens=2 delims==" %%i in ('set num_') do (set /a "var=1000000+%%i"
echo !var! >> nums.txt)
for /f %%j in ('sort nums.txt') do (set /a "var=%%j-1000000"
set "nums=!nums! !var!")
del /q nums.txt & echo !nums!
:eol

Resources