Editing text file to add "-" 8 characters in - batch-file

I deal with a large text files daily, I need to add a single "-" to each line 8 characters in for instance :
ABCDEF DC01 B738
ABCDEF B787
would become
ABCDEF -DC01 B738
ABCDEF - B787
How easy is this to do with a batch file?
regards
David

#echo off
setlocal enabledelayedexpansion
(for /f "delims=" %%a in (input.txt) do (
set line=%%a
set line=!line:~0,7!-!line:~7!
echo !line!
))>output.txt
Note: this will remove empty lines and has problems with exclamation marks. (may or may not be a problem; depends on your file content)
It also will eliminate lines that begin with ; due to default EOL character.
Also, lines are limited to ~8190 bytes max length (Thanks dbenham)

Here is a "robust" pure batch solution that preserves ! and empty lines. But it is still limited to ~8190 max line length, and it is painfully slow for large files. This will not modify lines that have less than 7 characters.
#echo off
setlocal disableDelayedExpansion
set "file=file.txt"
>"%file%.new" (
for /f "delims=" %%A in ('findstr /n "^"` "file.txt"') do (
set "s=%%A"
setlocal enableDelayedExpansion
set "s=!s:*:=!"
if not defined s (
echo(
) else if "!s:~7!" equ "" (
echo(!s!
else (
echo(!s:~0,7!-!s:~7!
)
endlocal
)
)
move /y "%file%.new" "%file%" >nul
And here is a truly robust and fast solution that uses JREPL.BAT - a regular text processing utility that is pure script (batch/JScript) that runs natively on any Windows machine from XP onward - no 3rd party executable required. This solution also does not modify lines that have fewer than 7 characters.
From the command line:
jrepl "^.{7}" "$&-" /f file.txt /o -
From within a batch script
call jrepl "^.{7}" "$&-" /f file.txt /o -

This is a simple two-lines Batch-JScript hybrid script solution that have all the advantages of JScript language: is fast, robust, manage all special characters and preserve empty lines. Besides, it is simple enough to be understood and then modified for other similar tasks after read the documentation. Copy it to a file with .BAT extension.
#set #a=0 // & cscript //nologo //E:JScript "%~F0" < input.txt > output.txt & goto :EOF
WScript.Stdout.Write(WScript.Stdin.ReadAll().replace(/^.{7}/gm,"$&-"));

Related

Merge two .txt files in batch in new lines, alternating one from each [duplicate]

This question already has answers here:
Interleave files with CMD using echo
(1 answer)
How can two text files be read in parallel by a batch file?
(1 answer)
Closed 3 years ago.
I want to merge two .txt files following a pattern that uses one line from file1, adds a break and follows with another from file2, then repeat.
I've tried to solve this in notepad++' replace function, but it seems out of its scope completely. I've had mixed success using batch (only useful tool I'm familiar with) managing to make the .bat file past the lines separately with linebreaks, but I can't find information on how to select one line from each file alternating instead of the whole file at once.
This is the .bat file I have so far:
#Echo off
( for /f "delims=" %%A in (
'Type 1.txt^&Echo:^&Type 2.txt'
) Do #Echo:%%A
) > 3.txt
Current output format:
entire file1.txt
entire file2.txt
Intended output format:
single line from file1.txt
single line from file2.txt
repeat
I continue researching but I'm not seeing much that can point me in the right direction, all help appreciated.
Windows command processor cmd.exe interpreting and executing a batch file is not designed for text file processing. It is designed for execution of commands and executables. There are lots of other scripting languages which would be better for this task than cmd.exe.
However, this small batch file works for this task with some limitations.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "LineNumber=0"
for /F usebackq^ delims^=^ eol^= %%I in ("file1.txt") do (
set /A LineNumber+=1
set "Line[!LineNumber!]=%%I"
)
set "LineNumber=0"
(for /F usebackq^ delims^=^ eol^= %%I in ("file2.txt") do (
set /A LineNumber+=1
call echo(%%Line[!LineNumber!]%%
echo(%%I
))>"file3.txt"
endlocal
The limitations are:
The number of non-empty lines must be equal in both input files.
The two input files do not contain empty lines which should be also written into output file as empty lines are ignored by FOR.
The lines in both input files do not contain exclamation marks as otherwise those lines are corrupted by enabled delayed expansion on double parsing of the command lines with %%I.
The first input file is not too large to be loaded into memory space for environment variables.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
echo /?
endlocal /?
for /?
set /?
setlocal /?
This will do it if the files aren't the same size. The batch file counts the lines in each file and compares the line counts. If one file has more lines than the other, the shorter one will output empty lines for missing lines in file with less lines.
Personally I would go this route then just fix the blank lines with PowerShell if I needed this script for myself.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "\\server\folder"
set "InputFile1=File1.txt"
set "InputFile2=File2.txt"
set "OutputFile=mix.txt"
(set Newline=^
%==%
)
if not exist "%InputFile1%" goto EndBatch
if not exist "%InputFile2%" goto EndBatch
del "%OutputFile%" 2>nul
for /F "delims=:" %%I in ('%SystemRoot%\System32\findstr.exe /R /N "^" "%InputFile1%"') do set "LineCount1=%%I"
for /F "delims=:" %%I in ('%SystemRoot%\System32\findstr.exe /R /N "^" "%InputFile2%"') do set "LineCount2=%%I"
rem Exchange input file 1 and 2 if input file 2 has more lines than input file 1.
if %LineCount2% GTR %LineCount1% set "InputFile1=%InputFile2%" & set "InputFile2=%InputFile1%"
(
for /F usebackq^ delims^=^ eol^= %%I in ("%InputFile1%") do (
set "LineFile1=%%I"
setlocal EnableDelayedExpansion
set /P LineFile2=
echo(!LineFile1!!Newline!!LineFile2!>>"%OutputFile%"
endlocal
)
)<"%InputFile2%"
:EndBatch
popd
endlocal
pause
Lines starting with a semicolon and containing an exclamation mark are also processed well by this script. Just empty lines in the two input files are ignored and cause unexpected content in output file.
[Mofi] made some edits to this version.
If you wanted to blank the lines with PowerShell like I suggested then save this as clearlines.ps1 and then run it from your batch file at the end.
$pathToFile = "\\server\folder"
(Get-Content -Path $pathToFile) -notmatch '(^[\s,-]*$)| (rows\s*affected)' | Set-Content -Path $pathToFile

How to get just the first line of a text file written into a new text file using a batch file?

Okay I have several lines in a text file. I want to get the first line and save it in another file. For example this is the text file:
put returns between paragraphs
for linebreak add 2 spaces at end
for linebreak add 2 spaces at end2
for linebreak add 2 spaces at end3
I want put returns between paragraphs to be saved into another file.
I used
for /f "tokens=" %%A in ('findstr /r "^[0-9][0-9]*$" <"C:\Users\Sherlock\Desktop\AbcImport\123.txt"') do echo 123>>1234.txt
pause
But it doesn't work at all.
How to get just the first line of a text file written into a new text file using a batch file?
Option 1 - SET /P : This is the simplest and fastest pure batch solution, provided the line does not exceed 1021 bytes, and it does not end with control characters that must be preserved. The size of the file does not matter - it will always read and write the first line very quickly.
#echo off
setlocal enableDelayedExpansion
set "ln="
<"input.txt" set /p "ln="
>"output.txt" (echo(!ln!)
Option 2 - FOR /F : This will work with lines up to ~8191 bytes long, but it can be slow if the file is really large because the FOR /F loop must read the entire file before it processes the first line. This solution is basically the same as the Mofi answer, except it disables the EOL option, so it never ignores the first line, regardless what the first character is. It does have a limitation that it will skip empty lines, so technically it does not give the correct result if the first line is empty:
#echo off
for /f usebackq^ delims^=^ eol^= %%A in ("input.txt") do echo(%%A>"output.txt"&goto :break
:break
There is a way to preserve the first line if it is empty using pure batch, but I would not bother. I would move on to ...
Option 3 - JREPL.BAT, or some other non-batch solution : Batch is quite poor at manipulating text files. You are much better off using some other scripting language like VBScript, JScript, or Powershell. Or a Windows port of any number of unix utilities.
I would use JREPL.BAT - a hybrid JScrpit/batch regular expression text processing utility that runs natively on any Windows machine from XP onward. It is way overkill for such a simple task, but it is an extremely handy, powerful, and efficient tool to have in your arsenal. Once you have it, then it can be used for many text processing tasks. Full documentation is embedded within the script.
jrepl "^.*" "$&" /jendln "quit=true" /f "input.txt" /o "output.txt"
Use CALL JREPL if you put the command within a batch script.
Here is the batch code to write just first non blank/empty line of a text file into another text file.
#echo off
for /F "usebackq delims=" %%I in ("InputTextFile.txt") do (
echo %%I>"OutputTextFile.txt"
goto ContinueAfterLoop
)
:ContinueAfterLoop
InputTextFile.txt is the file in current directory containing the first line to copy.
OutputTextFile.txt is the file created in current directory with first line from input file copied into this output file.
The command GOTO is used to exit the loop after first line is processed and continue the batch file below the loop.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
for /?
goto /?
Read also the Microsoft article about Using Command Redirection Operators.
You can use use this command:
SetLocal EnableDelayedExpansion
for /f "tokens=* delims=;" %%m in ("C:\Users\Sherlock\Desktop\AbcImport\123.txt") do (
set /p FirstLine=<%%m
echo !FirstLine!>>1234.txt
)
and for multiple file:
SetLocal EnableDelayedExpansion
for %%a in ("*") do (
for /f "tokens=* delims=;" %%m in ("%%a") do (
set /p FirstLine=<%%m
echo !FirstLine!>>1234.txt
)
)
rem Get the first line of a text file:
set /P "line=" < "C:\Users\Sherlock\Desktop\AbcImport\123.txt"
rem Write it into a new text file:
echo %line%> 1234.txt

Batch Script to overwrite a file with first 5 lines

I have a file with x lines and I would like to overwrite that file with only first 5 lines deleting the rest. I need to do this with a batch script. Any help on this would be greatly appreciated.
Thanks
Kumar
As long as each of first 5 lines is <= 1021 bytes long, and there are no control characters at the end of any of the 5 lines:
#echo off
setlocal enableDelayedExpansion
<"test.txt" >"test.txt.new" (
for /l %%N in (1 1 5) do (
set "ln="
set /p "ln="
echo(!ln!
)
)
move /y "test.txt.new" "test.txt" >nul
Or you could use my JREPL.BAT utility - a hybrid JScript/batch script that runs on any Windows machine from XP onward. This solution works well with any size file as long as no line exceeds 2 gigabytes - effectively no limitation.
JREPL.BAT is really designed to do regular expression search and replace on text files, but it is convenient to use it to get the head or tail of a file.
jrepl "^" "" /jbegln "quit=(ln>=5)" /f test.txt /o -
#echo off
set "file_to_process=E:\somefile.txt"
set "first_n_lines=5"
break>"%temp%\empty"&&fc "%temp%\empty" "%file_to_process%" /lb %first_n_lines% /t |more +4 | findstr /B /E /V "*****" >5.txt
rem move /y 5.txt "%file_to_process%"
remove the rem on the last line if 5.txt file is what you want.

How to write a single spacebar to a txt file with batch

I need to add a single spacebar to a textfile ussing batch
however the following dosnt work
ECHO >C:\txt.txt
this produces the text Echo is (off) instead???
Im ussing windows batch
echo is printing the text "ECHO if off" because you haven't provided any parameters to it. If you type echo /? for usage instructions you will see this behavior defined: "Type ECHO without parameters to display the current echo setting". So, in your case, echo is set to off.
If you want to have on the space character without a newline and carriage return you will most likely need to use set. If you are using Windows XP, this should be easy.
Windows XP
>txt.txt ( <nul set /p "= " )
If you are running Vista or Higher it keeps a little tricky because windows strips leading spaces on the set command.
Windows Vista or Higher
You will need to create a backspace character and then print that:
:: Create a backspace character
for /f %%A in ('"prompt $H &echo on &for %%B in (1) do rem"') do set BS=%%A
<nul set /p=%BS% > txt.txt
Note that this doesn't actually print a space, but a backspace character instead. I'm not quite sure if this will work for you, but I couldn't think of any easier way. Otherwise you are probably better off using some other scripting language, like python or powershell.
Echo a space (+ line break)
ECHO. > C:\txt.txt
Note that this will output a carriage return plus line break (an enter) as well. So your file will become 3 bytes.
Alternatively, you may create the file with a 0-byte using
fsutil file createnew c:\txt.txt 1
Only this doesn't add a space, but a 0 character.
Alternatively, you may create a file with a single space once, and copy that to txt.txt each time you need it to contain a space.
Use an external tool, like echon.exe from this zipfile (from this site). It mimics echo -n, like it is available in Linux.
To create a single space with Win Vista or above you can't use the SET/P command anymore (as dbenham mentioned).
But you can use the copy command
#echo off
setlocal EnableDelayedExpansion
call :createSub
call :echoWithoutLinefeed "=hello"
call :echoWithoutLinefeed " world"
exit /b
:echoWithoutLinefeed
> txt.tmp (echo(%~1!sub!)
copy txt.tmp /a txt2.tmp /b > nul
type txt2.tmp
del txt.tmp txt2.tmp
exit /b
:createSub
copy nul sub.tmp /a > nul
for /F %%a in (sub.tmp) DO (
set "sub=%%a"
)
del sub.tmp
exit /b
First the text is outputs with a simple ECHO but at the end a SUB character is appended, just before the ECHO appends CR LF.
And with copy inputfile /a outputfile /b all characters are copied up to the first SUB character.
Not only does SET /P not print leading white space on Vista and beyond, it also cannot print leading =.
Below is code that works on all modern versions of Windows from XP onward:
#echo off
setlocal enableDelayedExpansion
for /f %%A in ('copy /Z "%~dpf0" nul') do set EOL=%%A^
:: The above two empty lines are critical - DO NOT REMOVE
<nul set /p "=x!EOL! " >temp.tmp
findstr /v $ temp.tmp >file.txt
del temp.tmp
The above technique can be extended to support printing nearly any string without newline, with a few exceptions:
The string must not contain a carriage return
XP FINDSTR will display most control characters and many extended ASCII characters as dots. See What are the undocumented features and limitations of the Windows FINDSTR command? for more info.
Here are two methods for 32 bit Windows:
This method creates a file directly with hex 20 (space):
#echo off
(for %%v in (E100''20 Nfile.tmp RCX 01 W Q) do #echo %%v)|debug >nul
ren file.tmp "file with a single space.txt"
This one removes the two trailing characters from the file.tmp file
#echo off
set f=file.tmp
echo. >%f%
(for %%v in (E100''83''E9''02 L103 P N%f% W103 Q) do echo %%v)|debug %f% >nul
ren %f% "file with a single space.txt"
This will work on Vista and higher, any bitness.
#echo off
(
echo -----BEGIN CERTIFICATE-----
echo IA==
echo -----END CERTIFICATE-----
)>file.tmp
certutil -decode file.tmp "file with a single space.txt" >nul
del file.tmp
Here is the way to create a one byte single space file without creating a temporary file.
cmd /d /c (prompt=$S) ^& cmd /d /k <nul >SingleSpace.txt

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