I wrote the following batch file:
setlocal EnableDelayedExpansion
set integer=0
for /f "delims=" %%a in ('where /R C:\ /F python.exe') do (
set Paths[!integer!]=%%a
set /A integer+=1
)
for %%b in (%Paths%) do (
echo %%b
)
The idea is that I want to run where /R C:\ /F python.exe and then store the several paths outputted into an array, I need this because I need to handle these paths later on. So far for testing purposes I called the second half of the script just to make sure the array is working properly.
Your second part is wrong. Since there are no real arrays in batch scripting, you cannot use a for loop similar to something like for each element in array do as used in other languages. Refer to this post for details: Arrays, linked lists and other data structures in cmd.exe (batch) script.
To loop through all (pseudo-)array elements you could do the following instead:
set /A count=integer-1
for /L %%i in (0,1,%count%) do echo Paths[%%i] = !Paths[%%i]!
As suggested by Aacini in a comment, you might change your base-0 to a base-1 (pseudo-)array by moving the line set /A integer+=1 upwards to be before line set Paths[!integer!]=%%a in your first part, so the variable integer already contains the greatest index, which would lead to a slightly simpler second part:
for /L %%i in (1,1,%integer%) do echo Paths[%%i] = !Paths[%%i]!
Alternatively, you could use a for /F loop that parses the output of set Paths[:
for /F "tokens=1* delims==" %%i in ('set Paths[ 2^> nul') do echo %%i = %%j
This might be beneficial:
you do not need to know the total number of elements;
the indexes do not have to be adjacent, they could even be non-numeric;
But there are two caveats:
the (pseudo-)array elements are not returned in ascending order with respect to the indexes, because set Paths[ returns them alphabetically sorted, where the order is 0, 1, 10, 11,..., 2, etc., so the order becomes jumbled as soon as there are indexes with more than a single digit;
in case there exist other variables beginning with that name, like Paths[invalid] or Paths[none, they become returned too;
Related
i have a problem, i need a code as in title. purpose of the script: i need to copy the file with a different language code, f.e. XXX_x_xYYY_EN_xx to XXX_x_xYYY_ES_xx and XXX_x_xYYY_DE_xx. So far i have this, but it does not work:
setlocal enableDelayedExpansion
for /l %%H in (1,1,2) do (
set endf[%%H]=!fName[%%H]:*_EN_=!
set trim[%%H]=_EN_!endf[%%H]!
set beginf[%%H]=!fName[%%H]:%trim[%%H]%=!
)
fName is set elswere, its something like this
fName[1]=XXX_x_xYYY_EN_xx
fName[2]=XXXXX_x_xYYYY_EN_x
Everything works except the set beginf(it spits out "beginf[1]=fName[1]:="), i've tryed myriads of % nad ! combos. Fun fuct it works in this case (other script, same puprose but work for only one file in directory, i'd like to make it more versatile):
set beginf=!NAME:%langcode%=!
help :) thanks!
So you have two diferent conversions for two array elements, isn't it? So you just need to match each filename with its corresponding conversion, right?
Something like this, perhaps?
set "conv[1]=ES"
set "conv[2]=DE"
for /l %%H in (1,1,2) do (
for %%c in (!conv[%%H]!) do set "newName[%%H]=!fName[%%H]:_EN_=_%%c_!"
)
I suggest you to read this answer.
EDIT: New method added
This code do exactly the same than the one in your posted answer:
#echo off
setlocal EnableDelayedExpansion
for %%a in (*.xml) do (
set "fName=%%a"
for %%c in (ES DE PL) do (
copy "%%a" "!fName:_EN_=_%%c_!"
)
)
A comparison of your code vs. this one:
You really not need an array of file names. If you create the array just to process its elements once with no further processing, then you are wasting the space occupied by the array. You may do the same thing using an individual file name variable.
Also, you not need an array of conversions. If you just want to repeat a command with several conversions, then it is much simpler to use a list of conversions instead.
It is a bad idea to use special characters as FOR replaceable parameters (where the documentation specifies a letter). Batch files are intrinsically cryptic, so there is no need to include additional complexities...
If you just want to process all files (no rename they) then it is simpler to use a plain for command instead of a for /F one on a 'dir /B' command. The second form requires to execute an additional copy of cmd.exe program and to create a temporary disk file...
If the newfName (array) variable is used just to execute the copy command in the next line, then such a variable is (again) a waste of space. You may create the new name in the copy command itself.
I found the solution :)
Full script with file counting below:
#echo off
setlocal enableDelayedExpansion
set /a count=0
for /f %%# in ('dir *.xml /b') do (
set /a count+=1
set fName[!count!]=%%~xn#
)
set "conv[1]=ES"
set "conv[2]=DE"
set "conv[3]=PL"
for /l %%H in (1,1,!count!) do (
for /l %%G in (1,1,3) do (
for %%c in (!conv[%%G]!) do (
set "newfName[%%G]=!fName[%%H]:_EN_=_%%c_!"
copy !fName[%%H]! !newfName[%%G]!
)
)
)
loops clarification:
for /l %%H - loops through the fName array
for /l %%G - loops through the lang table (conv) array
for %%c - combine above loops and copy files with changed names
thanks #Aacini for your input, it put me on the right tracks :)
I have a text file with one string per line (only a couple lines total). This file needs to be searched and each string stored. The script will ultimately prompt the user to choose one of the stored strings (if more than one string/line is present in the file), but the for loop is iterating an extra time when I don't want it to.
I'm using a counter to check the number of iterations, and I also read that it's probably a NL CR regex issue, but the finstr /v /r /c:"^$" in the for file-set like in this post Batch file for loop appears to be running one extra time/iteration doesn't work for me (but I probably don't understand it correctly).
The "pref" term is because the strings are to be eventually used as a prefix of files in the same folder...
#echo off
setlocal enabledelayedexpansion
set /a x=1
for /f %%a in (sys.txt) do (
set pref!x!=%%a && echo %^%pref!X!%^% && set /a x+=1
)
echo last value of x = !x!
for /L %%a in (1,1,!x!) do (
echo !pref%%a!
)
REM The rest would be to prompt user to choose one (if multiple) and
REM then use choice as a prefix with a ren %%a %prefX%%%a
If the "sys.txt" contains three lines with strings A, B, C respectively, then the output I currently get is:
pref1
pref2
pref3
last value of x = 4
A
B
C
ECHO is off.
ECHO is off. is not desired, clearly.
You just need to change your increment structure like this. (set it before each line starting from a base of 0)
#Echo Off
Setlocal EnableDelayedExpansion
Set "i=0"
For /F "UseBackQ Delims=" %%A In ("sys.txt") Do (
Set/A "i+=1"
Set "pref!i!=%%A"
Echo(pref!i!)
Echo(last value of i = %i%
For /L %%A in (1,1,%i%) Do Echo(!pref%%A!
I have the following code (source identified) which is great, but it fails when a title contains a percent symbol. I like the fact that it doesn't use temp files and allows me to overwrite the original file. Any thoughts on how to fix this for percent symbols?
REM Randomize the Onesies playlist
REM Source: stackoverflow.com/questions/19393155
REM DOES NOT PROPERLY CREATE PLAYLIST ENTRY FOR ALBUMS CONTAINING PERCENT SYMBOLS (but it's fast)
for /f "delims=" %%a in (G:\Playlists\Onesies.m3u8) do call set "$$%%random%%=%%a"
(for /f "tokens=1,* delims==" %%a in ('set $$') do echo(%%b)>G:\Playlists\Onesies.m3u8
echo Playlist Onesies.m3u8 has been created in the folder G:\Playlists\
echo.
pause
The problematic part is call set "$$%%random%%=%%a". To explain why, let us start from the beginning.
Simply using set "$$%random%=%%a" would not work, because %random% would be expanded (read) when the entire for /F loop, or more generally spoken, the whole block of code, is parsed, so the same value would be returned for every loop iteration. The call trick, toghether with doubled percent signs around variables, introduces another parsing phase, so when the entire for /F loop is parsed, call set "$$%%random%%=%%a" is interpreted as call set "$$%random%=%a", because each %% in a batch file is interpreted as one literal %. Hence the random variable is not yet expanded.
For every loop iteration, the for /F variable becomes replaced by the current iteration value. call initiates another parsing phase as already mentioned, so %random% becomes replaced by a individual random number per loop iteration. Assuming that %a is abritrary string, the line is interpreted as call set "$$%random%=arbitrary string" first, and after the second parsing phase it becomes set "$$16384=arbitrary string", supposing %random% returns the number 16384.
Now let us take another example value for %a which contains a percent sign, like string with % sign: after the first parsing phase, we have call set "$$%random%=string with % sign"; after the second one, we get something like set "$$32767=string with sign". The percent sign disappears here, because the command line interpreter encounters a single % sign that cannot be paired with another one (where a variable name is expected in between them, like %random%), so it simply dismisses that character. In case %a contained two % signs, for instance, more % signs % here, the result would be more here (most likely), because SPACEsignsSPACE, being in between a pair of % signs, would be treated as the name of a (undefined, hence empty) variable.
To solve that issue, we need to avoid %a to be passed through a second parsing phase. This can be achieved by avoiding %%a to appear in the call command line.
So to overcome the loss of % signs in your script, you need to store %%a into an interim variable and to do the same double-% expansion within the call command line like you already do for random:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set /A "index=0"
for /f "usebackq delims=" %%a in ("G:\Playlists\Onesies.m3u8") do (
set "item=%%a"
call set "$$%%random%%.%%index%%=%%item%%"
set /A "index+=1"
)
> "G:\Playlists\Onesies.m3u8" (
for /f "tokens=1,* delims==" %%a in ('set $$') do echo(%%b
)
echo Playlist "Onesies.m3u8" has been created in the folder "G:\Playlists\".
echo/
endlocal
pause
An alternative method is to apply delayed expansion:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Toggling delayed expansion in the loop is necessary to not lose exclamation marks.
set /A "index=0"
for /f "usebackq delims=" %%a in ("G:\Playlists\Onesies.m3u8") do (
setlocal EnableDelayedExpansion
rem // This loop passes `random` over the `endlocal` barrier:
for /f %%b in ("!random!.!index!") do (
endlocal
set "$$%%b=%%a"
)
set /A "index+=1"
)
> "G:\Playlists\Onesies.m3u8" (
for /f "tokens=1,* delims==" %%a in ('set $$') do echo(%%b
)
echo Playlist "Onesies.m3u8" has been created in the folder "G:\Playlists\".
echo/
endlocal
pause
The original code from the question relies on the built-in random number variable random which may return duplicate values. This leads to the problem that some lines of the read file get lost.
To resolve this issue, both of the above approaches herein feature a counter called index which is of course unique for every single line that is read by the for /F loop. This counter value is appended to the random value so that the total concatenated string is unique. Therefore, none of the lines of the read file are lost any more, opposed to the original script.
I have this code
#Echo Off
SetLocal EnableDelayedExpansion
SET mydir=D:\
SET DirCount=2
SET T=
For /F %%i In (qqq.txt) Do (
set fg=%%i
FOR /L %%G IN (2, 1, %DirCount%) DO (call :subroutine "%%i")
)
:subroutine
Set T=!T!../
start /wait %mydir%program.exe %T%%fg%
echo %t%%fg% >>%cd%see.log
qqq.txt has rows
1
2
3
In result i get:
../1
../../2
../../../3
../../../../3
But I need like this:
../1
../../1
../2
../../2
../3
../../3
Please Help what I'm doing wrong?
Just a little additional requried
I need what first ocur in
set "T=!T!../"
will be without dots
set "T=!T!/"
result must be
/1
../1
../../1
/2
../2
../../2
how and where to add counter and make visible for each loop somthing like
echo Now looping: %%i row of %countrow(in qqq.txt), left %countrow-%%I
echo Now looping: Dir Nr%%G !T!%%i
Sorry for stupid questions but batch coding is difficult to me.
You have 3 problems:
1) You don't exit your program after the main loop so it falls through into the subroutine
2) Your FOR /L IN() clause is wrong, the first number should be 1, not 2
3) You need to reset T back to undefined within the outer loop.
EDIT
I believe you may have an additional problem. When redirecting output you prefix your file name with %cd%. I don't think that is giving the result you want. If your current directory is C:\test, then >>%cd%see.log will result in output sent to file named testsee.log in the root C:\ directory. If you wanted the output in the current directory then it should be >>%cd%\see.log, but the path info is not really needed since the default is to use the current directory. So really all you would need is >>see.log. Since I don't know your intent, I have left the redirection as originally written. End Edit
#echo Off
SetLocal EnableDelayedExpansion
SET mydir=D:\
SET DirCount=2
For /F %%i In (qqq.txt) Do (
SET T=
set fg=%%i
FOR /L %%G IN (1, 1, %DirCount%) DO (call :subroutine "%%i")
)
exit /b
:subroutine
Set T=!T!../
start /wait %mydir%program.exe %T%%fg%
echo %t%%fg% >>%cd%see.log
There is no need to call a subroutine in your case, especially since you have already enabled delayed expansion. The following gives the same result with less code, and it is faster because it avoids the relatively slow CALL statement. (performance is probably not an issue in this small example, but if using loops with many iterations it can make a huge difference)
#echo Off
SetLocal EnableDelayedExpansion
SET mydir=D:\
SET DirCount=2
For /F %%i In (qqq.txt) Do (
SET "T="
FOR /L %%G IN (1, 1, %DirCount%) DO (
set "T=!T!../"
start /wait %mydir%program.exe !T!%%i
echo !T!%%i >>%cd%see.log
)
)
Partial answer to additional questions
You can use FOR /F to capture the result of FIND /C to get the number of lines in "qqq.txt". This should be done before you enter the main loop:
for /f %%N in ('find /c /v "" ^<qqq.txt') do set "rowCount=%%N"
You can use SET /A to do basic math, thus enabling you to keep track of the number of remaining lines
First initialize the remaining count before entering the main loop:
set "remainingLines=%rowCount%"
Then within the main loop use SET /A to decrement the value.
set /a remainingLines-=1
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