How to calculate length of an environment variable - batch-file

This may have been solved, but I looked and couldn't find a satisfactory answer. How does one quickly calculate the length of an environment variable in a .bat script?
My problem involves environment variables of a couple thousand characters (I understand the practical limit is around 8k... the max size of a command line).
The straightforward approach simply counts characters. I'll use %path% below as an example. Assume the environment variable is known to exist -- which it does in my case -- so its length is at least 1. No special characters (eg double-quote) are involved:
#echo off& setlocal enabledelayedexpansion
set /a length=1
:loop
if not "!path:~%length%,1!"=="" set /a length+=1& goto loop
This calculates and leaves the value in environment variable 'length'.
I have seen such solutions, but they are extremely slow for longer length variables. A better approach (a couple order of magnitudes faster, and deterministic) is a binary search, such as:
#echo off& setlocal enabledelayedexpansion
set /a p2=16384, length=p2-1
:loop
if "!path:~%length%,1!"=="" set /a length-=p2
if !p2! geq 2 (set /a length+=p2/=2& goto loop) else set /a length+=1
I suppose I can live with that, but still I wonder if I'm being stupid and missing something obvious. I am looking for a pure .bat solution.
below added 6/12/2017
Learning from this method suggested by Compo led me to solution simpler/faster then dostips' (18% faster over all strings length 1 .. 8k). That seemed significant enough to post:
#echo off& setlocal enabledelayedexpansion
set "str=A%path%"
set length=0
for %%p in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if not "!str:~%%p,1!"=="" set "str=!str:~%%p!"& set /a length+=%%p
)

PURE BATCH
#echo off
del temp.txt
for /F "delims=:" %%G in ('findstr /N "<resource>" "%~F0"') do set "start=%%G"
(for /F "usebackq skip=%start% delims=" %%G in ("%~F0") do echo %%G) > temp.txt
rem echo everything after "<resource>" - code by #Aacini
for %%G in (temp.txt) do set /a size=%%~zG - 2
rem get the file length and remove the CR\LF count
echo %size%
pause
exit/b
<resource>
things to test length
and can be mutli-line
This script echoes the string to file, and use a for loop to retrieve the file size, which is the string length. To maximize speed, code-golf it.
Some facts:
It took around .54 seconds to process a 1.04 mb long string.
Getting the length of a 1.04 MB string is more efficient than getting a string with 1-byte length.
OLD POWERSHELL
You can bypass the 8191 bytes limit by calling Powershell.
echo $characters = "yourString" > temp.ps1
echo $x= $characters.length >> temp.ps1
echo write-host $x >> temp.ps1
powershell -command "C:\PathToPS1file\temp.ps1
del /f /s /q temp.ps1
This is a slow powershell method, and requires lots of escaping.

Related

How do I compare my free disk space with a specific number of bytes?

I have a .bat script where I need to compare my free disk space with exactly 18GB.
If it's lower or equal than 18GB, it should exit.
If It's greater, it should continue.
#echo off
setlocal
set maxSize=19327352832
for /f "tokens=3" %%a in ('dir c:\') do (
set bytesfree=%%a
)
set bytesfree=%bytesfree:,=%
Echo %bytesfree%
Echo %maxSize%
If %bytesfree% LEQ %maxSize% Echo You'll need to delete some stuff first & pause & exit
If %bytesfree% GTR %maxSize% Echo Everything ready
endlocal && set bytesfree=%bytesfree%
I have 5GB free on C:\, so it should say "Everything ready" but it says "You'll need to delete some stuff first" and I don't now what is wrong.
I'm very new to .bat so if I have unecessary code, please correct me.
Perhaps a wmic based batch-file would work for you:
#("%__AppDir__%wbem\WMIC.exe" LogicalDisk Where "DeviceID='C:' And FreeSpace>'18000000000'" Get FreeSpace /Value 2>NUL|"%__AppDir__%find.exe" "=">NUL&&(Echo Everything ready)||(Echo You'll need to delete some stuff first&"%__AppDir__%timeout.exe" /T 3 /NoBreak>NUL&Exit /B))&Pause
Change 18000000000 to 19327352832 as necessary.
Your problem is, that numbers in batch are limited to INT32 (that's about 2GB), so if returns unexpected results. As a workaround you can compare the numbers as strings (you need to make sure, they have the same number of digits). Note dir's switch /-c, which suprresses the thousand separators:
#echo off
setlocal
set "maxSize=0000019327352832"
for /f "tokens=3" %%a in ('dir /-c c:\') do (
set bytesfree=0000000000000000%%a
)
set bytesfree=%bytesfree:~-16%
Echo %bytesfree%
Echo %maxSize%
If "%bytesfree%" LEQ "%maxSize%" Echo You'll need to delete some stuff first & pause & exit
If "%bytesfree%" GTR "%maxSize%" Echo Everything ready
endlocal && set bytesfree=%bytesfree%
As an alternative, you could use the help of a "proper" programming language (like Powershell) for the math, but that's always slower and not really necessary here (as you can see)

How can I make sure its enough disk space in batch?

Does anyone know if its possible to make sure the disk has enough space on a server with batch?
I have a batch script that loops and in this I would like to add a part that makes sure there is enough space on the disk and gives me an message if its not. Like this, easily explained:
Check diskspace
If below 10mb free space
echo low diskspace (& probably delete temp files or something)
else continue
Thanks for any help!
This uses the output of the dir command to retrieve the free space. All the test is wapped inside a subroutine that receives as argument the "drive" to check and the number of bytes required. On exit it raises errorlevel when there is no enough free space.
Arithmetic in batch files is limited 2^31 signed integers, so to handle values greater than 2147483647 and operate with storage units over 2GB more code is required. To avoid this, all the information is indicated/retrieved in bytes, padded and compared as strings.
#echo off
setlocal enableextensions disabledelayedexpansion
call :checkFreeSpace d: 123456789 && echo OK || echo No space
call :checkFreeSpace "\\10.0.0.1\c$" 2000000000
if errorlevel 1 (
echo No space
) else (
echo OK
)
goto :eof
:checkFreeSpace drive spaceRequired
setlocal enableextensions disabledelayedexpansion
set "pad=0000000000000000000000000"
set "required=%pad%%~2"
for %%d in ("%~1\.") do for /f "tokens=3" %%a in ('
dir /a /-c "%%~fd" 2^>nul ^| findstr /b /l /c:" "
') do set "freeSpace=%pad%%%a"
if "%freeSpace:~-25%" geq "%required:~-25%" exit /b 0
exit /b 1
note: This method has one caveat. If the indicated target is a drive root and there is not any file/folder in it, the dir command will not found anything to list and the free space will not be included in its output, making the test fail as the free space will be 0

Trying to reformat a very large csv with a batch file

I have an application that exports data in the format:
1a,1b,1c1,1c2,1c3, ... (up to 1c100),1d1,1d2,1d3, ... (up to 1d100)
2a,2b,2c1,2c2,2c3, ... (up to 2c100),2d1,2d2,2d3, ... (up to 2d100)
etc.
and I am trying to reformat this into
1a,1b,1c1,1d1
1a,1b,1c2,1d2
.
.
1a,1b,1c100,1d100
2a,2b,2c1,2d1
2a,2b,2c2,2d2
etc.
I figured that if this can be done a row at a time I can just loop through the file. However I can't find a way of doing a single row with either tokens, a list, or even as a string function. There is too much data to process in a single operation (each value is about 12 chars). Tokens limit at (roughly) 64/202, a list at about 107/202 and a string at about 1000/2300
Does anyone know how this can be written into a new file?
I was trying things like:
#echo off
setlocal enableDelayedExpansion
set dimCnt=0
<example.csv (
set /p "dimList=" >nul
for %%D in (!dimList!) do (
set /a dimCnt+=1
set "dim[!dimCnt!]=%%D"
)
)
echo
for /l %%I in (3 1 102) do echo !dim[1]!,!dim[2]!,!dim[%%I]!
</code>
..besides the fact that I have missed out the last variable in the line (need to add 100 to it), I can't get more than about 80-110 values out of the list (I guess it depends on value string length)
#echo off
setlocal enableextensions enabledelayedexpansion
(for /f "tokens=1,2,* delims=," %%a in (example.csv) do (
set "data=%%c"
set "i=0"
for %%f in ("!data:,=" "!") do (
set /a "i+=1"
set "d[!i!]=%%~f"
)
set /a "end=!i!/2"
set /a "j=!end!+1"
for /l %%i in (1 1 !end!) do (
for %%j in (!j!) do echo %%a,%%b,!d[%%i]!,!d[%%j]!
set /a "j+=1"
)
)) > output.csv
endlocal
This iterates over the file, getting the first two tokens in the line (%%a and %%b), the rest of the line (%%c) is splitted and each value stored in an environment variable array (kind of). Then, the array is iterated from the start and from the middle, reading the needed values to append to %%a and %%b and generating output file.
#ECHO OFF
SETLOCAL
(
FOR /f "tokens=1,2,*delims=," %%a IN (u:\long.csv) DO (
SET rpta=%%a
SET rptb=%%b
CALL :rptcd %%c
)
)>newfile.txt
GOTO :EOF
:rptcd
SET /a lines=100
SET lined=%*
FOR /l %%x IN (1,1,99) DO CALL SET lined=%%lined:*,=%%
:loop
IF %lines%==0 GOTO :EOF
SET /a lines-=1
CALL SET lined=%lined:*,=%
FOR /f "delims=," %%x IN ("%lined%") DO ECHO %rpta%,%rptb%,%1,%%x&shift&GOTO loop
GOTO :eof
This should get you going - just need to change the input filename and output filename...
Your code does not work because SET /P cannot read more than 1023 bytes. At that point it returns the data read so far, and the next SET /P picks up where it left off. Adapting your code to compensate will be very difficult. You would be better off using FOR /F as in MC ND's answer. But beware, batch has a hard limit of 8191 characters per line in pretty much all contexts.
Better yet, you could use another scripting language like JScript, VBS, or PowerShell. Performance will be much better, and the code much more robust and far less arcane. I love working with batch, but it simply is not a good text processing language.

Save values batch file

I wrote this script to looking for volume with enough free space:
#echo on
setlocal
set gbsize=1,073,741,824
Set gbsize=%gbsize:,=%
for %%A in (A B C D) do (
for /f "tokens=3,4,5,*" %%B in ('dir %%A:\') do (
set bytesfree=%%B
set bytesfree=%bytesfree:,=%
if %%D == free If %bytesfree% gtr %gbsize% echo hi
)
)
My problem is that the bytesfree variable dosent save its value. the output is(echo is on)
C:\Users\Desktop>(
set bytesfree=**780,607,488**
set bytesfree=**23167987712**
if free == free If 23167987712 GTR 1073741824 echo hi
)
hi
looks like the bytesfree losed its value.
Can anyone please help? and provide some explantation?
thanks.
To expand on Joey's (original short) answer, the entire for expression is parsed at once, and the % expansion occurs at parse time. But the value you want isn't there until the DO clause has executed. That is why you need delayed expansion. Read the help on for by typing HELP FOR from the command line.
Based on the comment from your previous question https://stackoverflow.com/a/9096601/1012053, it looks like you are attempting to find the drive with the most free space > 1GB.
Your current code has a slight risk of including the wrong line from the DIR command. I've modified it to filter the output using FINDSTR with a regular expression.
EDIT - Also, the IF command cannot properly compare numbers that exceed 2147483647.
>if 2147483647 gtr 2147483646 echo greater
greater
>if 2147483648 gtr 2147483647 echo greater
>if 1000000000000 gtr 2147483647 echo greater
>if 2147483648 equ 2147483647 echo equal
equal
>if 1000000000000 equ 2147483647 echo equal
equal
So the numbers need to be 0 prefixed and the IF command must be forced to do a string comparison instead of a numeric comparison. I forced a string comparison by enclosing the 0 prefixed number in quotes.
#echo off
setlocal enableDelayedExpansion
set "gbsize=1,073,741,824"
set "gbsize=%gbsize:,=%"
set "maxfree=000000000000000"
set "maxdrive="
for %%A in (C D) do (
for /f "tokens=3" %%B in ('dir %%A:\^|findstr /r /c:"^ *.* *Dir(s).*bytes free$"') do (
set "bytesfree=000000000000000%%B"
set "bytesfree=!bytesfree:,=!"
set "bytesfree=!bytesfree:~-15!"
if "!bytesfree!" gtr "!maxfree!" (
set "maxfree=!bytesfree!"
set "maxdrive=%%A:"
)
)
)
for /f "tokens=* delims=0" %%A in ("%maxfree%") do set maxfree=%%A
echo Drive with max free space is %maxdrive% %maxfree% bytes free
if %maxfree% gtr %gbsize% echo That is more than 1GB
An alternate method using WMIC
#echo off
setlocal enableDelayedExpansion
set "gbsize=1,073,741,824"
set "gbsize=%gbsize:,=%"
set "maxfree=000000000000000"
set "maxdrive="
for /f "skip=1 tokens=1,2" %%A in ('wmic volume get DriveLetter^, FreeSpace') do (
if "%%B" neq "" (
set "bytesfree=000000000000000%%B"
set "bytesfree=!bytesfree:~-15!"
if "!bytesfree!" gtr "!maxfree!" (
set "maxfree=!bytesfree!"
set "maxdrive=%%A"
)
)
)
for /f "tokens=* delims=0" %%A in ("%maxfree%") do set maxfree=%%A
echo Drive with max free space is %maxdrive% %maxfree% bytes free
if %maxfree% gtr %gbsize% echo That is more than 1GB
Short answer:
Use
setlocal enabledelayedexpansion
instead of just setlocal and then use !bytesfree! to refer to the variable (just replace % by !).
Longer answer:
This is because cmd expands variables as it parses a command, not when the command is run (which, obviously happens after parsing). A command in this case could also be a complete for statement including the block after it. So all instances of %bytesfree% within the loop are replaced by their value before the loop, which happens to be an empty string.
Delayed expansion is a special form of variable expansion which expands variables when a command is run instead of when parsing it. It can be enabled with
setlocal enabledelayedexpansion
(within a batch file) or
cmd /v:on
(for a complete session, but not in a batch file, unless you don't want it to resume).
Then the syntax !bytesfree! is used to refer to variables which are then expanded just prior to running a command. So when setting and using the same variable within a block (for loop, if, etc.) you pretty much always want to use delayed expansion.

How to change the order of lines in a file using a batch script?

A have a text file that contains the results of a dir
dir "%local%" /b /a:d /s >> FolderList.txt
But I want to iterate in a For loop going from the last to the first line.
Since I believe this cannot be done in the For command, how can I generate a new file containing the same lines but in a inverse order?
You can't using the For command. But you can reverse the order of the dir listing that created the text file, using dir "%local%" /o-n /b /a:d /s >> FolderList.txt; the - means "reversed".
I like the general strategy of both of Aacini's original solutions, but as written they have problems (some trivial, some significant)
Original Aacini solution 1 using temp file with SORT:
Corrupts lines containing exclamation point (!)
Strips leading colon(s) (:) from each line
temp file creation using >> not as efficient as >
Uses default SORT maximum line length of 4096 bytes
Line count unnecessarily capped at 1 million
Doesn't actually provide the asked for solution (an actual file output)
Leaves behind the temporary file
Modified solution 1
Here is a version that fixes the problems. The only practical limitation is a maximum line length of 8180 bytes (characters). I'm not sure how high FINDSTR can count, but this solution will handle up to 999 billion lines. (I agree with Aacini, no one would ever want to wait for such a large file to finish using a batch solution) The line limit can easily be adjusted.
#echo off
setlocal DisableDelayedExpansion
set file="%~1"
set revfile="%~1.rev"
set tempfile="%temp%\revfile%random%.txt"
(
for /f "delims=" %%a in ('findstr /n "^" %file%') do (
set "ln=%%a"
setlocal EnableDelayedExpansion
for /f "delims=:" %%n in ("!ln!") do set "prefix=000000000000%%n"
echo !prefix:~-12!!ln:*:=!
endlocal
)
)>%tempfile%
(
for /f "delims=" %%a in ('sort /rec 8192 /r %tempfile%') do (
set "ln=%%a"
setlocal EnableDelayedExpansion
echo(!ln:~12!
endlocal
)
)>%revfile%
del %tempfile%
Aacini modified solution 1
Aacini dramatically improved the robustness and performance with a modified solution 1 using SET /P and multiple TEMP files. The SET /P solution eliminates the need for a looped SETLOCAL/ENDLOCAL toggle, but it does have a few limitations.
Lines must be terminated by <LF><CR> (normal for Windows, but Unix style is sometimes encountered in Windows world).
Lines must be <= 1024 characters
Control characters at end of line will be stripped.
Modified solution 1 take 2
If any of the above limitations are a problem, here is an adaptation of my 1st solution that uses multiple temp files. Like Aacinis modified solution, it performs linearly with file size. It is about 40% slower than Aacinis modified version.
#echo off
setlocal DisableDelayedExpansion
set file="%~1"
set revfile="%~1.rev"
set "tempfile=%temp%\revfile%random%.txt"
findstr /n "^" %file% >"%tempfile%.1"
(
for /f "usebackq delims=" %%a in ("%tempfile%.1") do (
set "ln=%%a"
setlocal EnableDelayedExpansion
for /f "delims=:" %%n in ("!ln!") do set "prefix=000000000000%%n"
echo !prefix:~-12!!ln:*:=!
endlocal
)
)>"%tempfile%.2"
sort /rec 8192 /r "%tempfile%.2" >"%tempfile%.3"
(
for /f "usebackq delims=" %%a in ("%tempfile%.3") do (
set "ln=%%a"
setlocal EnableDelayedExpansion
echo(!ln:~12!
endlocal
)
)>%revfile%
del "%tempfile%*"
Original Aacini solution 2 using environment variables:
Corrupts lines containing exclamation point (!)
Strips blank lines
Doesn't actually provide the asked for solution (an actual file output)
Modified solution 2
Here is a version that fixes the problems. The only known limitations are
A maximum line length between 8181 and 8190, depending on line number
A maximum file size slightly under 64MB.
This was my favorite solution because the file output can probably be eliminated by processing the file in the variables directly, thus completely avoiding the creation of any temporary file. Edit But based on info provided by Aacini, I learned it has severe performance problems as the environment grows. The problem is worse than Aacini realized - Even a simple SET command suffers dramatically with large environment sizes. I've posted a question regarding this phenomenon at DosTips. http://www.dostips.com/forum/viewtopic.php?f=3&t=2597 (I originally posted on SO, but apparently the question is too open ended for this site)
#echo off
setlocal disableDelayedExpansion
set file="%~1"
set revfile="%~1.rev"
set num=0
for /f "delims=" %%a in ('findstr /n "^" %file%') do (
set /a "num+=1"
set "ln=%%a"
setlocal enableDelayedExpansion
for %%n in (!num!) do for /f "delims=" %%b in (""!ln:*:^=!"") do endlocal&set "ln%%n=%%~b"'
)
setlocal enableDelayedExpansion
(
for /l %%n in (!num! -1 1) do echo(!ln%%n!
)>%revfile%
There are two relatively easy ways to sort a file in reversed order. The first one is a direct method over file contents: add line numbers to all lines, sort the file in reversed order, eliminate line numbers:
#echo off
setlocal EnableDelayedExpansion
rem Insert line numbers in all lines
for /F "tokens=1* delims=:" %%a in ('findstr /n ^^ %1') do (
set /A lineNo=1000000+%%a
echo !lineNo!:%%b>> tempfile.txt
)
rem Sort the file and show the result
for /F "tokens=1* delims=:" %%a in ('sort /r tempfile.txt') do (
echo Line %%a is %%b
)
The other method consist in load the file lines in a Batch array, that may be processed in any way you wish:
#echo off
setlocal EnableDelayedExpansion
rem Load file lines in a Batch array
set lineNo=0
for /F "delims=" %%a in (%1) do (
set /A lineNo+=1
set "line[!lineNo!]=%%a"
)
rem Process array elements in reversed order:
for /L %%i in (%lineNo%,-1,1) do (
echo Line %%i is !line[%%i]!
)
This last method works only if the size of the file is below 64 MB, because this is the limit for Batch variables.
Both methods can be modified to correctly process special characters (> < |).
HOWEVER
If you want to delete all the tree contents of a folder in bottom-up order, the "right" way to do that is via a recursive subroutine...
EDIT Answer to dbenham
As I wrote in my answer, the two methods I proposed can be modified to correctly process special characters and blank lines. In my answer I showed a general method to "change the order of lines" in reversed order paying no special attention on create an output file because the OP said in his own answer that "The objective was to reorder a list of folders to prevent problems while deleting them in sequence", so I thought that was enough to show him how to process the folders in reversed order. I also assumed that the list of folders:
Have not exclamation points (!).
Have not leading colons (:).
Folder names are shorter than 4096 bytes.
Have less than 1000000 lines.
Have not blank lines.
I even thought (and still think) that the method the OP want to use to delete a list of folders is not adequate, and I mentioned this point under a big HOWEVER in my answer proposing to use a recursive subroutine instead.
However it seems that dbenham thought that the original question was something similar to "What is the most efficient method to sort a large file in reversed order?" and criticize my methods because they lack of such features. For this reason, I should reply in terms of this new question (efficient method), right?
In first place, it's funny to me that dbenham critizice my methods because "Doesn't actually provide the asked for solution (an actual file output)", but in his own Modified solution 2 he wrote that "This is my favorite solution because the file output can probably be eliminated by processing the file in the variables directly, thus completely avoiding the creation of any temporary file". ???
The two methods proposed by dbenham have a serious problem in terms of efficiency that was already discussed in this question: the pair of setlocal EnableDelayedExpansion and endlocal commands are executed with every line of the file. If the file is large (i.e. 200 000 lines and about 8 MB, as in the previous mentioned question) the environment will be copied to a new memory area and then deleted, and this will be repeated for 200000 times! Of course, this task is time-consuming. This problem becomes worse in dbenham's Modified solution 2: as the processing of lines go on, the environment grow as it store the file contents at that point. In the last lines of the file an environment almost equal to the size of the whole file will be copied to a new memory area for every remaining line of the file. Of course, this is the worst way to achieve this process in terms of efficiency!
There is another way to process empty lines and special characters that don't require the setlocal EnableDelayedExpansion - endlocal pair. For details on this method and further discussion on efficient ways to process large files, see the previously mentioned question.
The following Batch files are my modified versions on "How to sort a large file in reversed order in an efficient way".
Modified solution 1: using temp file with SORT
#echo off
setlocal EnableDelayedExpansion
set revfile="%~1.rev"
set tempfile=%temp%\revfile%random%
rem Insert line numbers in all lines
findstr /n ^^ %1 > "%tempfile%1.txt"
find /c ":" < "%tempfile%1.txt" > "%tempfile%2.txt"
set /P lines=< "%tempfile%2.txt"
call :JustifyLineNumbers < "%tempfile%1.txt" > "%tempfile%2.txt"
del "%tempfile%1.txt"
rem Sort the file in reversed order
sort /rec 8192 /r "%tempfile%2.txt" /o "%tempfile%3.txt"
del "%tempfile%2.txt"
rem Remove line numbers
call :RemoveLineNumbers < "%tempfile%3.txt" > %revfile%
del "%tempfile%3.txt"
goto :EOF
:JustifyLineNumbers
for /L %%i in (1,1,%lines%) do (
set /A lineNo=1000000000+%%i
set /P line=
echo !lineNo!!line:*:=!
)
exit /B
:RemoveLineNumbers
for /L %%i in (1,1,%lines%) do (
set /P line=
echo !line:~10!
)
exit /B
This solution still have a limit of "only" 1147483647 lines (the maximum 32-bits signed positive integer minus the initial seed). Although this limit can be easily increased in the way suggested by dbenham, that modification imply a slower execution speed. The conclusion is: if you really want to reverse-sort a very large file don't use a Batch file, but a more efficient programming language (like C).
Modified solution 2: using a Batch variable array
#echo off
setlocal EnableDelayedExpansion
set revfile="%~1.rev"
set tempfile=%temp%\revfile%random%
rem Load file lines in a Batch array
findstr /n ^^ %1 > "%tempfile%1.txt"
find /c ":" < "%tempfile%1.txt" > "%tempfile%2.txt"
set /P lines=< "%tempfile%2.txt"
del "%tempfile%2.txt"
call :CreateArray < "%tempfile%1.txt"
del "%tempfile%1.txt"
rem Process array elements in reversed order:
(for /L %%i in (%lines%,-1,1) do echo=!ln%%i!) > %revfile%
goto :EOF
:CreateArray
for /L %%i in (1,1,%lines%) do (
set /P line=
set ln%%i=!line:*:=!
)
exit /B
EDIT A possible solution for large environment problem.
I devised an idea that may solve, at least in part, the performance problems of SET command caused by a very large environment. Let's suppose that the internal operation of SET VAR=VALUE command follow these steps:
When a new variable is defined with a value that exceed the current environment size, the environment is copied to a new area if the area beyond it is not available.
The new area is just large enough to receive the new variable. No additional space is reserved.
The important one: When a large variable is deleted, the remaining free space is NOT released. The environment memory block is never shrunk.
If previous steps are true, then the performance problems may decrease if we first reserve the desired environment space via large (8 KB) variables with the same name of the working variables. For example, to reserve 1024 KB we define 128 large variables; I suppose that the time required to define these 128 variables will be less than the time required to fill the same 1024 KB with shorter variables.
When the process is running, the definition of the first 128 working variables will take the time necessary to delete an 8 KB variable and define a shorter one, but for the variable 129 on the process must be faster because it just define a new variable in an already available space. To aid to this process, the variables must have names that place them at the end of the environment as dbenham indicated.
:ReserveEnvSpace sizeInKB
rem Define the first large variable (reserving 6 bytes for variable name)
rem (this method may be done in larger chunks until achieve the fastest one)
set z1=X
for /L %%i in (1,1,8184) do set z1=!z1!X
rem Define the rest of large variables
set /A lastVar=%1 / 8
for /L %%i in (2,1,%lastVar%) do set z%%i=!z1!
exit /B
You may use MEM /P command to check the size and placement of the environment memory block. In old MS-DOS (command.com) days the environment was placed after command.com, but if a resident program was placed after the environment, then it can't grow anymore. For this reason, the /E:nnnnn switch was provided in command.com to reserve a certain size in bytes for the environment.
I have no time to check this method for the rest of the day, but here it is for you!
The objective was to reorder a list of folders to prevent problems while deleting them in sequence.
I came up with the following algorithm. I accept suggestions to make it more efficient or better.
#ECHO off
setLocal EnableDelayedExpansion
:: File that contains a list of folders
set file_from=%~1
:: Destination file, that will contain the sorted list
if "%2"=="" (
set replace=1
set file_to=_%file_from%
) else (
set file_to=%~2
)
:: Create empty destination file
if exist "%file_to%" del "%file_to%"
copy NUL "%file_to%"
:: Temporary file
if exist ".\~Remaining.txt" del ".\~Remaining.txt"
copy "%file_from%" .\~Remaining.txt
:: Sort the order of folders
:while
set untouched=1
For /f "tokens=* delims=" %%a in (.\~Remaining.txt) Do (
:: check if line was already added
FindSTR /X /C:%%a "%file_to%"
if errorlevel 1 (
set untouched=0
:: check if folder contains sub-folders to be added
FindSTR /B /C:%%a\ .\~Remaining.txt
if errorlevel 1 (
:: remove current line from "~Remaining.txt"
FindSTR /V /B /E /C:%%a .\~Remaining.txt> .\~Remaining_new.txt
move .\~Remaining_new.txt .\~Remaining.txt
:: add current line to destination file
>> "%file_to%" ECHO %%a
goto while
)
)
)
if untouched LSS 1 (
goto while
)
if exist .\~Remaining.txt del .\~Remaining.txt
if defined replace (
ECHO REPLACE!
:: destination was not provided, so replace
if exist "%file_from%" del "%file_from%"
move "%file_to%" "%file_from%"
)
This code will reverse a text file, but with a few limitations. Blank lines are omitted and lines containing special charaters cause it to fail: & < > |
#Echo Off
If "%1"=="" Goto Syntax
If "%2"=="" Goto Syntax
If Not Exist %1 (
Echo File not found: %1
Exit /B 2
)
SetLocal EnableDelayedExpansion
Set SOF=~StartOfFile~
Set InFile=%~snx1~in
Set OutFile=%2
Set TempFile=%~snx1~temp
If Exist %OutFile% Del %OutFile%
If Exist %TempFile% Del %TempFile%
Copy %1 %InFile% >nul
:Loop
Set "Line=%SOF%"
For /F "tokens=*" %%a In (%InFile%) Do (
If Not "!Line!"=="%SOF%" Echo !Line!>>%TempFile%
Set "Line=%%a"
)
Echo %Line%>>%OutFile%
Del %InFile%
If Not Exist %TempFile% (
EndLocal
Exit /B 0
)
Rename %TempFile% %InFile%
Goto Loop
:Syntax
Echo Usage:
Echo %~n0 input-file output-file
Echo.
Exit /B 1

Resources