Guest User

JUDAGO STR_MATH v1.3

a guest
Nov 19th, 2015
317
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Batch 23.13 KB | None | 0 0
  1. - JUDAGO Backup of STR_MATH v1.3
  2. - Numbered Variables - Variables that start with numbers.
  3. - Variable expansion - Why Variables Don't Change in Loops.
  4. - Nesting - How to Nest Variables.
  5.  by Einstein1969
  6.  
  7. @ECHO Off
  8. if "%~3"=="" (
  9.     echo STR_MATH.BAT v1.3
  10.     echo.
  11.     echo Large number and floating point work around script
  12.     echo for Addition, Subtraction, Division and Multiplaction.
  13.     echo Floating point and negitive numbers supported.
  14.     echo.
  15.     echo Usage:
  16.     echo STR_MATH.BAT {Number} {+-X} {Number}
  17.     echo STR_MATH.BAT {Number} / {Number} [n max decimal places]
  18.     echo.
  19.     echo Division defaults to a maximum ten decimal places of output.
  20.     echo An optional user defined max can be input as the fourth number.
  21.     echo No rounding is performed on output of division, any remainder
  22.     echo is discarded.
  23.     echo.
  24.     echo Output Will be echo'd, a "for /f" loop can be used to
  25.     echo capture the output.
  26.     echo.
  27.     set /p =-Judago OCT - DEC 2011- Send bugs to strmathnew.t.remel<nul
  28.     echo @antichef.com
  29.     echo Provided "AS-IS"
  30.     echo Only tested on XP
  31.     goto :eof
  32. )
  33.  
  34. SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
  35. set "_INP_OP=%~2"
  36. set "_INP_NUM1=%~1"
  37. set "_INP_NUM2=%~3"
  38. set _DIV_DP=10
  39.  
  40. for %%a in (INT DP DP1 DP2 LEN LEN1 LEN2 ZERO NEG NEG1 NEG2) do set _INP_%%a=
  41.  
  42. rem ***** Start Input validation *****
  43.  
  44. if "%~2"=="/" if not "%~4"=="" (
  45.     for /f "delims=0123456789 tokens=1*" %%a in ("A0%~4") do (
  46.         if "%%b"=="" (
  47.             for /f "tokens=1* delims=0" %%c in ("A0%~4") do (
  48.                 if "%%d"=="" (
  49.                     set _DIV_DP=0
  50.                 ) else set _DIV_DP=%%d
  51.             )
  52.         ) else 1>&2 Echo ERROR: "%~4" not detected as a valid number of decimal places  &exit /b 1
  53.     )
  54.     shift /4
  55. )
  56.        
  57. if not "%~4"=="" 1>&2 echo ERROR: Too many arguments: "%~4"&exit /b 1
  58. if not defined _INP_NUM1 1>&2 echo ERROR: Invalid Input: "!_INP_NUM1!"&exit /b 1
  59. if not defined _INP_NUM2 1>&2 echo ERROR: Invalid Input: "!_INP_NUM2!"&exit /b 1
  60. if not defined _INP_OP 1>&2 echo ERROR: Invalid Operator: "!_INP_OP!"&exit /b 1
  61. if not "!_INP_OP:~1!"=="" 1>&2 echo ERROR: Invalid Operator: "!_INP_OP!"&exit /b 1
  62.  
  63. rem ***** / ***** Start Negitive input ***** \ *****
  64. for %%a in (1 2) do (
  65.     if "!_INP_NUM%%a:~0,1!"=="-" if not "!_INP_NUM%%a:~1!"=="" (
  66.         set _INP_NUM%%a=!_INP_NUM%%a:~1!
  67.         set _INP_NEG%%a=-
  68.     ) else 1>&2 echo ERROR: Invalid input: "!_INP_NUM%%a!"&exit /b 1
  69. )
  70. rem ***** \ ***** End Negitive input ***** / *****
  71.  
  72. for /f "tokens=1* delims=0123456789." %%a in ("A0!_INP_NUM1!!_INP_NUM2!") do (
  73.     if not "%%b"=="" 1>&2 echo ERROR: Invalid input: "!_INP_NUM1!" **OR** "!_INP_NUM2!"&exit /b 1
  74. )
  75. for %%a in (1 2) do (
  76.     for /f "tokens=1-3 delims=." %%b in ("0!_INP_NUM%%a!0") do (
  77.         if not "%%d"=="" 1>&2 echo ERROR: Invalid input: "!_INP_NUM%%a!"&exit /b 1
  78.     )
  79. )
  80. for /f "tokens=1* delims=/Xx+-" %%a in ("A+!_INP_OP!") do (
  81.     if not "%%b"=="" 1>&2 echo ERROR: Invalid operator: "!_INP_OP!"&exit /b 1
  82. )
  83. rem ***** End input validation *****
  84.  
  85. rem ***** Start Remove leading zero's / Return for zero sums  *****
  86. for %%a in (1 2) do (
  87.     for /f "tokens=1* delims=0" %%b in ("A0!_INP_NUM%%a!") do (
  88.         if "%%c"=="" (
  89.             if /i "!_INP_OP!"=="x" echo 0
  90.             if "!_INP_OP!"=="/" (
  91.                 if %%a==2 (
  92.                     1>&2 Echo ERROR: Divide by zero
  93.                     exit /b 1.
  94.                 ) else echo 0
  95.             )
  96.             if "!_INP_OP!"=="-" (
  97.                 if %%a==1 (
  98.                     if defined _INP_NEG2 (
  99.                         echo !_INP_NUM2!
  100.                     ) else echo -!_INP_NUM2!
  101.                 ) else (
  102.                     echo !_INP_NEG1!!_INP_NUM1!
  103.                 )
  104.             )
  105.             if "!_INP_OP!"=="+" (
  106.                 if %%a==1 (
  107.                     echo !_INP_NEG2!!_INP_NUM2!
  108.                 ) else echo !_INP_NEG1!!_INP_NUM1!
  109.             )
  110.             exit /b 0
  111.         ) else set _INP_NUM%%a=%%c
  112.     )
  113. )
  114. rem ***** End Remove leading zero's / Return for zero sums *****
  115.  
  116. rem ***** Start Floating point normalisation *****
  117. for %%a in (1 2) do (
  118.     if "!_INP_NUM%%a:~0,1!"=="." set _INP_NUM%%a=0!_INP_NUM%%a!
  119.     if "!_INP_NUM%%a:~-1!"=="." set _INP_NUM%%a=!_INP_NUM%%a!0
  120.     for /l %%b in (0 1 9) do set _INP_NUM%%a=!_INP_NUM%%a:%%b=%%b !
  121.     for %%c in (!_INP_NUM%%a!) do set /a _INP_LEN%%a+=1
  122.     set _INP_NUM%%a=!_INP_NUM%%a: =!
  123.     if "!_INP_NUM%%a!"=="!_INP_NUM%%a:.=!" (
  124.         set _INP_DP%%a=0
  125.     ) else (
  126.         for /l %%d in (!_INP_LEN%%a! -1 1) do (
  127.             if not defined _INP_DP%%a if "!_INP_NUM%%a:~%%d,1!"=="." (
  128.                 set /a _INP_DP%%a=!_INP_LEN%%a! - %%d
  129.             )
  130.         )
  131.     )
  132.     set _INP_NUM%%a=!_INP_NUM%%a:.=!
  133. )
  134.  
  135. if !_INP_DP1! gtr !_INP_DP2! (
  136.     set /a _INP_DP=_INP_DP1 - 1
  137. ) else set /a _INP_DP=_INP_DP2 - 1
  138.  
  139. for /l %%a in (!_INP_DP1! 1 !_INP_DP!) do set _INP_NUM1=!_INP_NUM1!0
  140. for /l %%a in (!_INP_DP2! 1 !_INP_DP!) do set _INP_NUM2=!_INP_NUM2!0
  141. rem ***** End Floating point normalisation *****
  142.  
  143. rem ***** Start Negitive output checking *****
  144. if /i "!_INP_OP!"=="x" (
  145.     if "!_INP_NEG1!!_INP_NEG2!"=="-" set _INP_NEG=-
  146. ) else if "!_INP_OP!"=="+" (
  147.     if "!_INP_NEG1!!_INP_NEG2!"=="--" set _INP_NEG=-
  148.     if defined _INP_NEG2 if not defined _INP_NEG1 (
  149.         call :isgreater !_INP_NUM1! !_INP_NUM2!
  150.         if "!_GTR_RES!"=="3" (
  151.             set _INP_NUM1=%_INP_NUM2%
  152.             set _INP_NUM2=%_INP_NUM1%
  153.             set _INP_NEG=-
  154.         )
  155.         set _INP_OP=-
  156.     )
  157.     if defined _INP_NEG1 if not defined _INP_NEG2 (
  158.         call :isgreater !_INP_NUM1! !_INP_NUM2!
  159.         if "!_GTR_RES!"=="3" (
  160.             set _INP_NUM1=%_INP_NUM2%
  161.             set _INP_NUM2=%_INP_NUM1%
  162.         ) else if "!_GTR_RES!"=="1" set _INP_NEG=-
  163.         set _INP_OP=-
  164.     )
  165. ) else if "!_INP_OP!"=="-" (
  166.     if "!_INP_NEG1!!_INP_NEG2!"=="" (
  167.         call :isgreater !_INP_NUM1! !_INP_NUM2!
  168.         if "!_GTR_RES!"=="3" (
  169.             set _INP_NUM1=%_INP_NUM2%
  170.             set _INP_NUM2=%_INP_NUM1%
  171.             set _INP_NEG=-
  172.         )
  173.     )
  174.     if "!_INP_NEG1!!_INP_NEG2!"=="--" (
  175.         call :isgreater !_INP_NUM1! !_INP_NUM2!
  176.         if "!_GTR_RES!"=="3" (
  177.             set _INP_NUM1=%_INP_NUM2%
  178.             set _INP_NUM2=%_INP_NUM1%
  179.         ) else if "!_GTR_RES!"=="1" set _INP_NEG=-
  180.     )
  181.     if defined _INP_NEG2 if not defined _INP_NEG1 set _INP_OP=+
  182.     if defined _INP_NEG1 if not defined _INP_NEG2 (
  183.         set _INP_NEG=-
  184.         set _INP_OP=+
  185.     )
  186. ) else if "!_INP_OP!"=="/" (
  187.     if "!_INP_NEG1!!_INP_NEG2!"=="--" set _INP_NEG=
  188.     if "!_INP_NEG1!!_INP_NEG2!"=="-"  set _INP_NEG=-
  189. )
  190. rem ***** End Negitive output checking *****
  191.  
  192. if "!_INP_OP!"=="-" goto subtract
  193. if "!_INP_OP!"=="+" goto add
  194. if /i "!_INP_OP!"=="X" goto multiply
  195. if "!_INP_OP!"=="/" goto divide
  196. goto help
  197.  
  198. :SUBTRACT
  199. if "%~1"=="@" (
  200.     set _SUB_NUM1=%~2
  201.     set _SUB_NUM2=%~3
  202. ) else (
  203.     set _SUB_NUM1=!_INP_NUM1!
  204.     set _SUB_NUM2=!_INP_NUM2!
  205. )
  206. for %%a in (CHA RES LEN1 LEN2 CAR TOT) do set _SUB_%%a=
  207. for %%a in (1 2) do (
  208.     for /l %%b in (0 1 9) do set _SUB_NUM%%a=!_SUB_NUM%%a:%%b=%%b !
  209.     for %%c in (!_SUB_NUM%%a!) do set /a _SUB_LEN%%a+=1
  210.     set _SUB_NUM%%a=!_SUB_NUM%%a: =!
  211. )
  212. if !_SUB_LEN1! gtr !_SUB_LEN2! (
  213.     set /a _SUB_LEN=_SUB_LEN1 - 1
  214. ) else set /a _SUB_LEN=_SUB_LEN2 - 1
  215. for /l %%b in (!_SUB_LEN1! 1 !_SUB_LEN!) do set _SUB_NUM1=0!_SUB_NUM1!
  216. for /l %%b in (!_SUB_LEN2! 1 !_SUB_LEN!) do set _SUB_NUM2=0!_SUB_NUM2!
  217. for /l %%a in (!_SUB_LEN! -1 0) do (
  218.     set /a _SUB_RES=!_SUB_NUM1:~%%a,1! - !_SUB_NUM2:~%%a,1!
  219.     if !_SUB_RES! lss 0 (
  220.         set _SUB_TAKE=
  221.         for /l %%b in (%%a -1 0) do if not defined _SUB_TAKE (
  222.             if not "%%b"=="%%a" (
  223.                 if not "!_SUB_NUM1:~%%b,1!"=="0" (
  224.                     set /a _SUB_CHA=!_SUB_NUM1:~%%b,1! - 1
  225.                     set /a _SUB_TAKE=%%b + 1
  226.                     for %%c in (!_SUB_TAKE!) do set _SUB_NUM1=!_SUB_NUM1:~0,%%b!!_SUB_CHA!!_SUB_NUM1:~%%c!
  227.                     set /a _SUB_RES=1!_SUB_NUM1:~%%a,1! - !_SUB_NUM2:~%%a,1!
  228.  
  229.                 ) else (
  230.                     set /a _SUB_CHA=%%b + 1
  231.                     for %%c in (!_SUB_CHA!) do set _SUB_NUM1=!_SUB_NUM1:~0,%%b!9!_SUB_NUM1:~%%c!
  232.                 )
  233.             )
  234.         )
  235.     )
  236.     set _SUB_TOT=!_SUB_RES!!_SUB_TOT!
  237. )
  238. for /f "tokens=1* delims=0" %%a in ("A0!_SUB_TOT!") do set _SUB_TOT=%%b
  239. if not defined _SUB_TOT set _SUB_TOT=0
  240. if not "%~1"=="@" (
  241.     call :FINISH _SUB_TOT
  242.     echo %_INP_NEG%!_SUB_TOT!
  243. )
  244. goto :eof
  245.  
  246. :ADD
  247. if /I "%~1"=="@" (
  248.     set _ADD_NUM1=%~2
  249.     set _ADD_NUM2=%~3
  250. ) else (
  251.     set _ADD_NUM1=!_INP_NUM1!
  252.     set _ADD_NUM2=!_INP_NUM2!
  253. )
  254. for %%a in (LEN1 LEN2 CAR TOT) do set _ADD_%%a=
  255. for %%a in (1 2) do (
  256.     for /l %%b in (0 1 9) do set _ADD_NUM%%a=!_ADD_NUM%%a:%%b=%%b !
  257.     for %%c in (!_ADD_NUM%%a!) do set /a _ADD_LEN%%a+=1
  258.     set _ADD_NUM%%a=!_ADD_NUM%%a: =!
  259. )
  260. if !_ADD_LEN1! gtr !_ADD_LEN2! (
  261.     set /a _ADD_LEN=_ADD_LEN1 - 1
  262. ) else set /a _ADD_LEN=_ADD_LEN2 - 1
  263. for /l %%b in (!_ADD_LEN1! 1 !_ADD_LEN!) do set _ADD_NUM1=0!_ADD_NUM1!
  264. for /l %%b in (!_ADD_LEN2! 1 !_ADD_LEN!) do set _ADD_NUM2=0!_ADD_NUM2!
  265. for /l %%a in (!_ADD_LEN! -1 0) do (
  266.     set /a _ADD_CAR=_ADD_CAR + !_ADD_NUM1:~%%a,1! + !_ADD_NUM2:~%%a,1!
  267.     set _ADD_TOT=!_ADD_CAR:~-1!!_ADD_TOT!
  268.     set _ADD_CAR=!_ADD_CAR:~0,-1!
  269.     if not defined _ADD_CAR set _ADD_CAR=0
  270. )
  271. if !_ADD_CAR! gtr 0 set _ADD_TOT=!_ADD_CAR!!_ADD_TOT!
  272. if not "%~1"=="@" (
  273.     Call :FINISH _ADD_TOT
  274.     echo %_INP_NEG%!_ADD_TOT!
  275. )
  276. goto :eof
  277.  
  278. :MULTIPLY
  279. set /a _INP_DP=(_INP_DP * 2) + 1
  280. set _MUL_NUM1=!_INP_NUM1!
  281. set _MUL_NUM2=!_INP_NUM2!
  282. for %%a in (CAR TOT) do set _MUL_%%a=0
  283. for %%a in (X10 REV1 REV2) do set _MUL_%%a=
  284. for %%a in (1 2) do (
  285.     for /l %%b in (0 1 9) do set _MUL_NUM%%a=!_MUL_NUM%%a:%%b=%%b !
  286.     for %%c in (!_MUL_NUM%%a!) do set _MUL_REV%%a=%%c !_MUL_REV%%a!
  287. )
  288. for %%a in (!_MUL_REV1!) do (
  289.     set _MUL_ROW=!_MUL_X10!
  290.     for %%b in (!_MUL_REV2!) do (
  291.         set /a _MUL_CAR=^(%%a * %%b^) + _MUL_CAR
  292.         set _MUL_ROW=!_MUL_CAR:~-1!!_MUL_ROW!
  293.         set _MUL_CAR=!_MUL_CAR:~0,-1!
  294.         if not defined _MUL_CAR set _MUL_CAR=0
  295.     )
  296.     for /f "tokens=1* delims=0" %%c in ("A0!_MUL_CAR!!_MUL_ROW!") do (
  297.         if not "%%d"=="" (
  298.             call :ADD @ %%d !_MUL_TOT!
  299.             set _MUL_TOT=!_ADD_TOT!
  300.         )
  301.     )
  302.     set _MUL_CAR=
  303.     set _MUL_X10=!_MUL_X10!0
  304. )
  305. Call :FINISH _MUL_TOT
  306. echo %_INP_NEG%!_MUL_TOT!
  307. goto :eof
  308.  
  309. :DIVIDE
  310. for %%a in (DONE LEN1 LEN2 X10 PAD) do set _DIV_%%a=
  311. for /l %%a in (1 1 %_DIV_DP%) do set _DIV_PAD=0!_DIV_PAD!
  312. set /a _INP_DP=_DIV_DP - 1
  313. set _DIV_NUM1=!_INP_NUM1!!_DIV_PAD!
  314. set _DIV_NUM2=!_INP_NUM2!
  315. set _DIV_TOT=0
  316. set _DIV_LOOP=10
  317. for %%a in (1 2) do (
  318.     for /f "tokens=1* delims=0" %%d in ("A0!_DIV_NUM%%a!") do set _DIV_NUM%%a=%%e
  319.     for /l %%b in (0 1 9) do set _DIV_NUM%%a=!_DIV_NUM%%a:%%b=%%b !
  320.     for %%c in (!_DIV_NUM%%a!) do set /a _DIV_LEN%%a+=1
  321.     set _DIV_NUM%%a=!_DIV_NUM%%a: =!
  322. )
  323. set /a _DIV_LEN1-=1
  324. for /l %%a in (!_DIV_LEN2! 1 !_DIV_LEN1!) do (
  325.     set _DIV_X10=0!_DIV_X10!
  326.     set /a _DIV_LOOP+=10
  327. )
  328. for /l %%a in (0 1 !_DIV_LOOP!) do (
  329.     if not defined _DIV_DONE (
  330.         call :ISGREATER !_DIV_NUM1! !_DIV_NUM2!!_DIV_X10!
  331.         if !_GTR_RES! leq 2 (
  332.             call :SUBTRACT @ !_DIV_NUM1! !_DIV_NUM2!!_DIV_X10!
  333.             set _DIV_NUM1=!_SUB_TOT!
  334.             call :ADD @ !_DIV_TOT! 1!_DIV_X10!
  335.             set _DIV_TOT=!_ADD_TOT!
  336.         ) else if defined _DIV_X10 (
  337.             set _DIV_X10=!_DIV_X10:~1!
  338.         ) else set _DIV_DONE=1
  339.     )
  340. )
  341. Call :finish _DIV_TOT
  342. if "!_DIV_TOT!"=="0" (
  343.     echo 0
  344.     1>&2 echo ERROR: Insufficient decimal places^(!_DIV_DP!^)
  345. ) else (
  346.     echo !_INP_NEG!!_DIV_TOT!
  347. )
  348. Goto :eof
  349.  
  350. :FINISH
  351. set _FIN_NUM=!%~1!
  352. set _FIN_FLAG=
  353. if not defined _INP_DP (
  354.     set _INP_DP=0
  355. ) else set /a _INP_DP+=1
  356. if not "!_INP_DP!"=="0" (
  357.     for /l %%a in (1 1 !_INP_DP!) do if "!_FIN_NUM:~%_INP_DP%!"=="" set _FIN_NUM=0!_FIN_NUM!
  358.     set _FIN_NUM=!_FIN_NUM:~0^,-%_INP_DP%!.!_FIN_NUM:~-%_INP_DP%!
  359.     for /l %%a in (!_INP_DP! -1 1) do (
  360.         if "!_FIN_NUM:~-1!"=="0" set _FIN_NUM=!_FIN_NUM:~0^,-1!
  361.         if "!_FIN_NUM:~-1!"=="." set _FIN_NUM=!_FIN_NUM:~0^,-1!
  362.     )
  363. )
  364. for /f "tokens=1* delims=0" %%a in ("A0!_FIN_NUM!") do (
  365.     if "%%b"=="" (
  366.         set _FIN_NUM=0
  367.     ) else set _FIN_NUM=%%b
  368. )
  369. if "!_FIN_NUM:~0,1!"=="." set _FIN_NUM=0!_FIN_NUM!
  370. IF NOT DEFINED _FIN_NUM SET _FIN_NUM=0
  371. set %1=!_FIN_NUM!
  372. goto :eof
  373.  
  374.  
  375. :ISGREATER
  376. for /f "tokens=1* delims=0" %%a in ("A0%~1") do (
  377.     if "%%b"=="" (
  378.         set _GTR_NUM1=0
  379.     ) else set _GTR_NUM1=%%b
  380. )
  381. for /f "tokens=1* delims=0" %%a in ("A0%~2") do (
  382.     if "%%b"=="" (
  383.         set _GTR_NUM2=0
  384.     ) else set _GTR_NUM2=%%b
  385. )
  386. for %%a in (RES lEN1 lEN2) do set _GTR_%%a=
  387. for %%a in (1 2) do (
  388.     for /l %%b in (0 1 9) do set _GTR_NUM%%a=!_GTR_NUM%%a:%%b=%%b !
  389.     for %%c in (!_GTR_NUM%%a!) do set /a _GTR_lEN%%a+=1
  390.     set _GTR_NUM%%a=!_GTR_NUM%%a: =!
  391. )
  392. if !_GTR_lEN1! gtr !_GTR_lEN2! (
  393.     set _GTR_RES=1
  394. ) else if !_GTR_lEN2! gtr !_GTR_lEN1! (
  395.     set _GTR_RES=3
  396. ) else (
  397.     set /a _GTR_lEN1-=1
  398.     for /l %%a in (0 1 !_GTR_lEN1!) do (
  399.         if not defined _GTR_RES (
  400.             if !_GTR_NUM1:~%%a^,1! gtr !_GTR_NUM2:~%%a^,1! set _GTR_RES=1
  401.             if !_GTR_NUM2:~%%a^,1! gtr !_GTR_NUM1:~%%a^,1! set _GTR_RES=3
  402.         )
  403.     )
  404. )
  405. if not defined _GTR_RES set _GTR_RES=2
  406. goto :eof
  407.  
  408. ######
  409.  
  410. This is a verbatim copy of a how to I posted on another site a while ago. I assure you I wrote every word, I just didn't post it here first:
  411.  
  412.  
  413.  
  414. Content of this page:
  415.  
  416. Numbered Variables - Variables that start with numbers.
  417. Variable expansion - Why Variables Don't Change in Loops.
  418. Nesting - How to Nest Variables.
  419.  
  420.  
  421.  
  422.  
  423.  
  424. Batch Script Variable Expansion, Win2000 and Up.
  425.  
  426. Variable expansion in Windows 2000 and higher batch scripts.
  427.  
  428. A great deal of questions in the Programming forum are related to problems caused by the nature of variable expansion in batch scripts. Here I will attempt to outline some of the most common issues.
  429.  
  430.  
  431. My variable doesn't seem to work for some reason???
  432.  
  433.  
  434. Does the variable in question start with a number?
  435.  
  436. Batch variables shouldn't start with numbers, in some circumstances they will work, but it's more trouble than it's worth. Variables that start with numbers conflict with parameters, lets say we have a variable called "2var", we can normally set to it fine but when expanding "%2var%" it is interpreted as the batch parameter "%2" then the text "var%";. A delayed expansion variable !2var! will work fine in most cases.
  437.  
  438. It is also worth noting that "set /a", flatout refuses to assign to variables that start with numbers and won't perform arithmetic on them unless they are expanded.
  439.  
  440.  
  441.  
  442. Why doesn't [insert variable name] change in my for loop/if statement?
  443.  
  444.  
  445. Firstly you must understand the manner that variables are expanded and how batch scripts are interpreted.
  446.  
  447. Batch scripts are read line-by-line into the command processor cmd.exe(or command.com for DOS and Win9x). Any variables are replaced with their values and then the line is executed; variables are updated after each line is interpreted.
  448.  
  449. Enter code blocks: A group of commands enclosed in brackets is a code block. They are most commonly seen trailing for loops or if statements but can be used on there own. Code blocks are treated differently in terms of variable expansion, they are treated as one line. This means any variables that are set inside the block don't get updated until the block has finished execution, in the case of for loops the variables aren't updated until the entire loop has finished executing.
  450.  
  451. Here's an example:
  452.  
  453. Normal code:
  454.  
  455.  
  456.  
  457. set test=Hello!
  458. echo %test%
  459.  
  460. The result is:
  461.  
  462.  
  463. Hello!
  464.  
  465. Inside a code block:
  466.  
  467.  
  468. set test=Goodbye!
  469. if 1==1 (
  470.     set test=Hello!
  471.     echo %test%
  472. )
  473.  
  474. The result is:
  475.  
  476.  
  477. Goodbye!
  478.  
  479. It may seem like an unexpected result but you must consider that %test% was expanded to what it was set to before the code block was executed because it is considered one line.
  480.  
  481. Windows 2000 and up has a way to combat this problem, delayed expansion. To use delayed expansion add this to the top of the script:
  482.  
  483.  
  484. setlocal enabledelayedexpansion
  485.  
  486. With that in place any variables that need to be set to and updated within a code block should be marked with ! exclamation marks instead of % percentage signs. In the script above if delayed expansion was enabled "echo !test!" would have yielded "Hello!".
  487.  
  488. Unfortunately delayed expansion has it's problems:
  489.  
  490. 1. Recursion, if a script calls itself a way to avoid executing "setlocal enabledelayedexpansion" on every recursion is important because setlocal can only nest a finite number of times. If you call setlocal too many times you may get an error along the lines of "maximum setlocal recursion level reached".
  491.  
  492. 2. Where is my exclamation mark^^!, with delayed expansion enabled exclamation marks must be prefixed with two ^^ carets if they don't mark a variable. This can be a real problem when the exclamation marks are contained in a file a for /f loop is iterating over because they disappear right out of the for loop variable %%! %%! %%!. Variables that already contain ! exclamation marks before delayed expansion is enabled are unaffected, as are variables obtained using "set /p".
  493.  
  494. 3. Environment variable vs local environment variable, unfortunately there is, to the best of my knowledge, no way to use delayed expansion with out using setlocal. If a script uses setlocal all variables set within it are reset to their original states on exit, often without even calling endlocal. This can be a mixed blessing, sometimes you don't need the variable returned, other times it can be very useful....
  495.  
  496. If there are just a few variables you want returned it is possible to use the default expansion to your advantage; you can return as many as you want in this fashion:
  497.  
  498.  
  499. endlocal & set variable=%variable%
  500.  
  501.  
  502. or
  503.  
  504.  
  505. (
  506.     endlocal
  507.     set variable=%variable%
  508. )
  509.  
  510.  
  511.  
  512.  
  513. How do I nest variables?
  514.  
  515. Spoiler: This may take a while, the rest of this how to it nesting related.
  516.  
  517.  
  518. There are actually a couple of ways to nest variables, some even work right back to DOS, others make use of delayed expansion. It's even possible to go a couple of levels deep.
  519.  
  520. All of these examples are for batch scripts as opposed to the interactive command line, nesting still works on the command line but the number of % percentage signs is different.
  521.  
  522. The call method:
  523.  
  524. Firstly you need to know that % single percentage signs expand variables and %% double percentage signs expand to a single percentage(it's to do with the way input is parsed). Call can work with some commands to expand/update variables multiple times on a single line, just be aware that it only works with some commands. Set and echo are two commands calling will work for, while others like if won't work. If(he he) your command won't take this abuse you can set an intermediate variable, just use set the way echo is used below.
  525.  
  526. It starts simple then turns into a headache quickly as each level reduces two %% percentage signs to one and the next level does the same.
  527.  
  528. There are probably better ways to work this out, but here is one way:
  529.  
  530. The bottom nested variable is the one you want to display/use it requires one % percentage sign on either side, double it and add one for the next level(three on each side now), the next level requires you to double this and add one(now it's seven each side), and so on. Each time you do this you must also add an extra call to the front of the command.
  531.  
  532. Here are a couple of examples:
  533.  
  534.  
  535. set level1=Hello!
  536. set level2=level1
  537. call echo %%%level2%%%
  538.  
  539. set level1=Hello!
  540. set level2=level1
  541. set level3=level2
  542. call call echo %%%%%%%level3%%%%%%%
  543.  
  544. set level1=Hello!
  545. set level2=level1
  546. set level3=level2
  547. set level4=level3
  548. call call call echo %%%%%%%%%%%%%%%level4%%%%%%%%%%%%%%%
  549.  
  550. Following this method it is possible to go through many levels (any sane person would probably find another way) so naturally I kept going, the last level that worked was level11!(on xp sp3) The line length was 4161 characters, level12 yielded "Line input too long" - Geeze I think I'm going to write a compliant or something ;)
  551.  
  552. That's great you say, but I have a variable that when combined with another will make one super variable (which I shall rule the world with! BU WA HA HA HA!) well I got you supervillains covered too.
  553.  
  554. The call method works with these too; the most common use is numbered variables so that's the example I will use here.
  555.  
  556.  
  557. set num=1
  558. set var=test
  559. set test1=Hello!
  560. call echo %%%var%%num%%%
  561.  
  562. I only used one call here because "var" and "num" both expand the first time and %test1% expands the second time. If you want to further nest/expand the above principles still apply, but you must remember that %test1% counts as a level, and that it still needs the % percentage signs to expand test1 from %var% and %num%.
  563.  
  564.  
  565. set num=1
  566. set var=test
  567. set test1=level1
  568. set level1=Hello!!
  569. call call echo %%%%%%%var%%num%%%%%%%
  570.  
  571.  
  572. Although the above can happen it's more likely that the base variable is constant.
  573.  
  574.  
  575. set num=1
  576. set test1=Hello!!
  577. call echo %%test%num%%%
  578.  
  579. It's even more common that you have x variables that a numbered, for this I suggest the for /l loop:
  580.  
  581.  
  582. set test0=Greetings good friend!
  583. set test1=Hello My friend!
  584. set test2=Um..........
  585. set test3=How about that local sports team?
  586. set test4=Um.......I don't like sports.
  587. set test5=........Oh...........
  588. set test6=How about this weather?
  589. set test7=Yeah..........
  590. set test8=um......goodbye.....
  591. set test9=...oh..uh..yeah...goodbye.....
  592. set num=9
  593. for /l %%a in (0,1,%num%) do call echo %%test%%a%%
  594.  
  595. This brings me to an important point, alternatives to delayed expansion; if you don't want to/can't use it delayed expansion call can help. Just treat a variable like it's nested but don't add the extra % percentage sign because you will expand the contents as a variable instead of displaying/using the contents.
  596.  
  597.  
  598. set test=Goodbye already!
  599. (
  600.     set test=Hello!!
  601.     call echo %%test%%
  602.     echo %test%
  603. )
  604.  
  605. Your command, unlike echo or set, won't take it you say? Using set is just as useless because they are set in the same code block? for /f can help, just do your command(in place of echo) with %%~a instead of "%test%". Some special characters will have problems, but only if "test" contains any double quotes.
  606.  
  607.  
  608. set test=Well well.....
  609. (
  610.     set test=Hello!
  611.     for /f "delims=" %%a in ('call echo "%%test%%"') do echo %%~a
  612.     echo %test%
  613. )
  614.  
  615.  
  616. Enter delayed expansion:
  617.  
  618. Delayed expansion makes nesting variables much easier for a single level. Simply use ! exclamation marks on the outer variable and either standard variable(s) or text and standard variable(s) within. This should cover what I was rambling on about before:
  619.  
  620.  
  621. setlocal enabledelayedexpansion
  622. set level1=Hello!
  623. set level2=level1
  624. set num=2
  625. echo !%level2%!
  626. echo !level%num%!
  627. for /l %%a in (1,1,2) do echo !level%%a!
  628.  
  629. Now what happens if you want to combine two variables both set in the same code block?
  630.  
  631.  
  632. setlocal enabledelayedexpansion
  633. (
  634.     set part1=Hello^^!
  635.     set num=1
  636.     for /f "delims=" %%a in ("!num!") do echo !part%%a!
  637. )
Advertisement
Add Comment
Please, Sign In to add comment