adding a new column to csv and populate the values alternatives - batch-file

I am trying to write a batch file in windows which copies / appends a new column at the starting of CSV file . and then populates with values 0 and 1 alternately
For Example:
F1,F2,F3
1,2,3
1,2,3
2,3,4
3,4,5
Now I wish to add a new column at first and add values to them
ex
F0,F1,F2,F3
0,1,2,3
1,1,2,3
0,2,3,4
1,3,4,5
Just append 0 for all even row numbers and 1 for all odd rows
Below is the code that I have written, but that just adds 0 to all rows, but I want 0 and 1 alternately
#echo off > newfile.csv & setLocal enableDELAYedeXpansion
for /f "tokens=* delims= " %%a in (J_CAFE27032018_090325.csv) do (
>> newfile.csv echo a,%%a
)
A c equivalent would be having a for loop for all even and odd columns
for(i=0;i<n;i+2)
{
add 0
}
for(i=0;i<n;i++)
{
add 0
}
Would you please help me with the batch file equivalent to traverse each odd and even rows.

This method is the same as Aacini's however it prepends the header line with #,, (can be modified).
#Echo Off & SetLocal EnableDelayedExpansion
Set "i=" & (For /F "UseBackQ Delims=" %%A In ("J_CAFE27032018_090325.csv") Do (
If Defined i (Echo !i!,%%A) Else Set "i=1" & Echo #,%%A
Set /A "i=(i+1)%%2"))>newfile.csv & Exit /B

This is one of several ways to do it:
#echo off & setLocal enableDELAYedeXpansion
set "i=0"
(for /f "delims=" %%a in (J_CAFE27032018_090325.csv) do (
echo !i!,%%a
set /A "i=(i+1)%%2"
)) > newfile.csv
However your original logic dos not correctly process the header (first) line...

#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET "filename1=%sourcedir%\q50041056.txt"
SET "outfile=%destdir%\outfile.txt"
SET "firstline=Y"
SET "zerostart=Y"
(
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO (
IF DEFINED firstline (
ECHO F0,%%a
SET "firstline="
) ELSE (
IF DEFINED zerostart (
ECHO 0,%%a
SET "zerostart="
) ELSE (
ECHO 1,%%a
SET "zerostart=Y"
)
)
)
)>"%outfile%"
GOTO :EOF
You would need to change the settings of sourcedir and destdir to suit your circumstances.
I used a file named q50041056.txt containing your data for my testing.
Produces the file defined as %outfile%
The usebackq option is only required because I chose to add quotes around the source filename.
This solution uses the fact that if defined interprets the current status of the variablename, so the variable in question is simply toggled between a value and nothing.

I would do it the following way -- given that no line of the input CSV file (data.csv) exceeds an overall length of 1021 characters/bytes:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=%~1" & rem // (CSV file to process; use first argument)
set "_SEP=," & rem // (separator character)
set "_HEAD=F0" & rem // (header text for new column)
set /A "_MOD=2" & rem // (divisor for modulo operation)
set /A "_OFF=0" & rem // (offset for modulo operation)
setlocal EnableDelayedExpansion
rem // Determine number of lines contained in CSV file:
for /F %%C in ('^< "!_FILE!" find /C /V ""') do set /A "COUNT=%%C"
rem // Read from CSV file:
< "!_FILE!" (
rem // Check whether header text is defined:
if defined _HEAD (
rem // Header text defined, so read current header:
set "LINE=" & set /P LINE=""
rem // Prepend header text for new column to current line:
echo(!_HEAD!!_SEP!!LINE!
rem // Decrement number of lines:
set /A "COUNT-=1"
)
rem // Process remaining lines in a loop:
for /L %%I in (1,1,!COUNT!) do (
rem // Read current line:
set "LINE=" & set /P LINE=""
rem // Perform modulo operation:
set /A "NUM=(%%I+_OFF-1)%%!_MOD!"
rem // Prepend remainder of division to current line:
echo(!NUM!!_SEP!!LINE!
)
)
endlocal
endlocal
exit /B
This approach uses input redirection to read from the input CSV file.
To write the output to another CSV file, say data-mod.csv, rather than to the console, use the following command line, assuming the script is called prepend-modulo.bat and the input CSV file is named data.csv, and both reside in the current directory:
prepend-modulo.bat "data.csv" > "data_mod.csv"

Related

How to get text from a specific line in Batch

I'd like to write a batch script that reads a specific word between delimiters:
Eg my text file contains the below
!DATA
Scen|2022|m|YTD|a|b|c|d|e|f|g|h|101
Scen|2022|m|YTD|a1|b1|c1|d1|e1|f1|g1|h1|102
The text file will have around 1000 lines. I will always want to read the 3rd line.
I want to:
1. Get the first word before the pipe delimiter (eg Scen) and store to some variable
2. I then want the second word between 1st & 2nd pipe marks (eg 2022)
Any help in this would be great.
Here's an example for you to really get your teeth into:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "targetLine="
( Set /P "="
Set /P "="
Set /P "targetLine="
) 0< "textFile.ext"
If Not Defined targetLine GoTo :EOF
For /F "Delims==" %%G In ('"(Set Field) 2>NUL"') Do Set "%%G="
Set "i=1"
SetLocal EnableDelayedExpansion
Set "Field!i!=%targetLine:|=" & Set /A i +=1 & Set "Field!i!=%"
Echo %%Field1%% = %Field1%; %%Field2%% = %Field2%
Pause
The above technique has actually defined a variable for each of the pipe delimited fields. To see those just change line 13 to (Set Field) 2>NUL
(Set /P "var=") 0< "stdInput" defines var with the value of the first non empty input line, In this case you would change stdInput (textfile.ext) to your actual source file. I have therefore extended the idea to retrieve the third line. I will not explain the creation of the variables for each of the fields, simply link you to an already existing explanation.
If these are your intended executions:
!DATA // Your Line Skipped: 1
Scen|2022|m|YTD|a|b|c|d|e|f|g|h|101 // Your Line Skipped: 2
Scen|2022|m|YTD|a1|b1|c1|d1|e1|f1|g1|h1|102 // Your Line Strings To Save: Scen & 2022
... // Your Lines Remaining Skipped: Goto %:^)
A simpler way to do it would be:
#echo off & cd /d "%~dp0"
for /f "usebackq skip=2 delims=| tokens=1-2*" %%i in (`
type InputFile.txt`)do set "_str_1=%%i" && set "_str_2=%%j" & goto %:^)
%:^)
echo\ %%_str_1%% == %_str_1% && echo\ %%_str_2%% == %_str_2%
Another, perhaps simpler, method:
#echo off
setlocal EnableDelayedExpansion
rem Read the 3rd line
( for /L %%i in (1,1,3) do set /P "line3=" ) < test.txt
rem Get 1st and 2nd words from line 3
set "goto="
set "word1=%line3:|=" & !goto! & set "goto=goto continue" & set "word2=%"
:continue
echo word1="%word1%", word2="%word2%"

How to read a comma delimited file and output results by 3 in a single row

I have a text file that contains more than 25k records which looks like this in a single line:
cr:121,cr:122,cr:123,cr:124,cr:221,cr:222,cr:223,cr:224,cr:225,cr:321, and so on..
I would like to create a batch file to read the comma separated values and output them in a new file by batch of 3.
Expected Result: newfile.txt
cr:121,cr:122,cr:123
cr:124,cr:221,cr:222
cr:223,cr:224,cr:225
cr:321
I searched the site but all I can find is batch commands for FOR statements that tackles files with multiple rows, not a single line.
I am currently trying
for /f "usebackq tokens=1-4 delims=," %%a in ("algtest_extract.txt") do (
echo %%a %%b %%c %%d )
but this only works for files with multiple lines. It gives me the first 4 values then exits.
In notepad++ press Ctrl+H, set Search Mode in "Regular expression", then use search pattern
([^,]+,[^,]+,[^,]+),
and repace with
\1\n
UPD
use search pattern ((?:[^,]+,){500}) if you want to use 500 elements instead of 3.
The commas in the end of each line could be replaced using search pattern ,$
I could not resist to write a batch script for accomplishing your task. Given script is called reshape.bat, provide the input text file as a command line argument, like this:
reshape.bat "algtest_extract.txt"
To store the output data into another file, say algtest_reshaped.txt, do this:
reshape.bat "algtest_extract.txt" > "algtest_reshaped.txt"
Here is the code (see all the explanatory remarks):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=%~1" & rem // (file to process; `%~1` means first argument)
set "_COL=3" & rem // (number of columns in the output data)
set "_SEP=," & rem // (separator character for input and output data)
set "_PAD=" & rem // (if defined, fill up last row with empty cells)
rem // Read from file:
< "%_FILE%" call :PROCESS
endlocal
exit /B
:PROCESS
rem // Initialise variables:
set "REST=" & rem // (remaining text string after the first separator)
set "LINE=" & rem // (row collector for line/row to output)
set "IDX=0" & rem // (column counter for output data)
setlocal EnableDelayedExpansion
:READ
rem // Read some text, 1023 characters/bytes at most:
set "STR=" & set /P STR=""
rem // Terminate loop if no more data are available:
if not defined STR goto :END
rem // Precede with potential remainder from previous loop:
set "STR=!REST!!STR!"
:PARSE
rem // Extract everything behind the first separator:
if defined STR set "REST=!STR:*%_SEP%=!"
rem // No separator had been found, so read more text:
if "!REST!"=="!STR!" goto :READ
rem // Extract part in front of first separator:
for /F "delims=%_SEP%" %%I in ("ITEM=!STR!") do set "%%I"
rem // Increment column counter:
set /A "IDX+=1" & if !IDX! lss %_COL% (
rem // Output line/row not yet complete, so go on assembling:
set "LINE=!LINE!%_SEP%!ITEM!"
) else (
rem // Output line/row complete, hence return it:
echo(!LINE:*%_SEP%=!%_SEP%!ITEM!
rem // Reset row collector and column counter:
set "LINE=" & set /A "IDX=0"
)
rem // Keep on parsing using the remainder:
set "STR=!REST!" & goto :PARSE
:END
rem // Return potential remaining data:
if defined _PAD (set /A "IDX=_COL-IDX-1") else (set /A "IDX=0")
set "LINE=!LINE!%_SEP%!REST!"
for /L %%I in (1,1,%IDX%) do set "LINE=!LINE!%_SEP%"
if defined LINE echo(!LINE:*%_SEP%=!
endlocal

New CSV by combining 2 csv files

I have 2 CSV files that has
File1:
Column1,column2
data1, data2
File2:
Column3,column4, column5,column6
data3,data4,data5,data6
I have to create a new CSV file that combines both columns from file 1 with the 1st and 3rd columns from file 2 (4 total columns).
Column1,column2,column3,column5
data1,data2,data3,data5
I am looking to do this using batch file commands. Any suggestions?
Code i am using helps me copy one file.
#ECHO OFF
SETLOCAL
(
FOR /f "tokens=1-3delims=," %%a IN (file1.csv) DO (
ECHO(%%a,%%c
)
)>new.csv
GOTO :EOF
How about following script?
File1.csv :
column1,column2
data1,data2
data3,data4
data5,data6
File2.csv :
column3,column4,column5,column6
data3,data4,data5,data6
data7,data8,data9,data10
Script :
#echo off
setlocal enabledelayedexpansion
set ct1=0
for /f "tokens=*" %%i in (File1.csv) do (
set /a ct1+=1
set ar1[!ct1!]=%%i
)
set ct2=0
for /f "tokens=*" %%i in (File2.csv) do (
set /a ct2+=1
set ar2[!ct2!]=%%i
)
if !ct1! lss !ct2! (
set ct=!ct2!
) else (
set ct=!ct1!
)
for /l %%i in (1,1,!ct!) do (
echo !ar1[%%i]!,!ar2[%%i]!>> new.csv
)
new.csv :
column1,column2,column3,column4,column5,column6
data1,data2,data3,data4,data5,data6
data3,data4,data7,data8,data9,data10
data5,data6,
Here is a pure batch solution that works, but with the following limitations and or assumptions:
File 1 lines are terminated by carriage return and linefeed (Windows style)
File 1 lines are no longer than 1021 bytes
File 2 must have a value for each column (no consecutive commas)
File 2 line lengths never exceed ~8191 bytes
File 2 does not have any quoted column values that include commas.
Files 1 and 2 have the same number of lines
Neither file has quoted data values that include new lines (rare, but possible within a CSV).
#echo off
setlocal disableDelayedExpansion
<"file1.csv" >"merged.csv" (
for /f "usebackq eol=, delims=, tokens=1,3" %%A in ("file2.csv") do (
set /p "part1="
set "part2=%%A,%%B"
setlocal enableDelayedExpansion
echo !part1!,!part2!
endlocal
)
)
A much more robust and faster solution is possible if you use PowerShell, JScript, or VBS.
You can also implement an efficient and robust solution using JREPL.BAT - a regular expression text processing utility. JREPL.BAT is pure script (hybrid batch/JScript) that runs natively on any Windows machine from XP onward. Full documentation is available from the command line via jrepl /?, or jrepl /?? for paged help.
This JREPL solution has only the following reasonable limitations:
Files 1 and 2 must have the same number of lines
Neither file has quoted data values that include new lines
#echo off
setlocal
set "col=\q(?:\q\q|[^,\q])*\q|[^,]*"
call jrepl "^(%col%),(?:%col%),(%col%)(?:,.*|$)" ^
"$txt=stdin.ReadLine()+','+$1+','+$2" ^
/jq /x /f "file2.csv" /o "merged.csv" <"file1.csv"
This flexible script does what you want, given that the following restrictions are not violated:
both files must contain the same number of lines;
the number of columns per line/row must be equal per each file;
lines are no longer than 1023 bytes, including the terminating line-break;
field/cell values must not contain line-breaks;
each line/row must be terminated by Windows-style line-breaks (CR+LF);
the given column numbers to copy must be sorted in ascending order;
So here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE1=%~dp0File1.csv" & rem // (1st input CSV file; state `%~1` to use 1st arg.)
set "_FILE2=%~dp0File2.csv" & rem // (2nd input CSV file; state `%~2` to use 2nd arg.)
set "_COLS1=1,2" & rem // (ascending list of columns to copy from 1st file)
set "_COLS2=1,3" & rem // (ascending list of columns to copy from 2nd file)
set "_SEPAR=," & rem // (separator character, usually `,`)
rem // Main routine:
4< "%_FILE1%" 3< "%_FILE2%" (
call :READLINE
)
endlocal
exit /B
:READLINE
rem // Read a line of both files:
set "LINE1=" & set "LINE2="
<&4 set /P LINE1=""
<&3 set /P LINE2=""
rem // Terminate sub-routine in case both lines are empty:
if not defined LINE1 if not defined LINE2 exit /B
rem // Process lines:
call :PROCESS LINE1 LINE2
rem // Repeat reading:
goto :READLINE
exit /B
:PROCESS ref_string1 ref_string2
setlocal DisableDelayedExpansion
set "BUF=%_SEPAR%"
setlocal EnableDelayedExpansion
rem // Test both strings against global wild-card characters:
set "STR1=!%~1!" & set "STR2=!%~2!"
if "!STR1:**=!!STR2:**=!"=="!STR1!!STR2!" goto :PROCESS_CONT
if "!STR1:*?=!!STR2:*?=!"=="!STR1!!STR2!" goto :PROCESS_CONT
if "!STR1:*<=!!STR2:*<=!"=="!STR1!!STR2!" goto :PROCESS_CONT
if "!STR1:*>=!!STR2:*>=!"=="!STR1!!STR2!" goto :PROCESS_CONT
>&2 echo(ERROR: Illegal character encountered^^!
exit /B 1
:PROCESS_CONT
rem // Prepare line strings for being processed by a standard `for` loop:
set "STR1=!STR1:"=""!^"
set "STR2=!STR2:"=""!^"
set "STR1="!STR1:%_SEPAR%=","!""
set "STR2="!STR2:%_SEPAR%=","!""
rem // `for /F` loops to transport prepared line strings beyond `endlocal`:
for /F "delims=" %%E in (^""!STR1!"^") do (
for /F "delims=" %%F in (^""!STR2!"^") do (
endlocal
rem // Process 1st line string:
set /A "IDX=0"
for %%I in (%%~E) do (
rem // Compare column index of current item with given column list:
set /A "IDX+=1" & set "FND="
for %%J in (%_COLS1%) do (
setlocal EnableDelayedExpansion
if !IDX! EQU %%J (
endlocal & set "FND=#"
) else endlocal
)
rem // Matching column index encountered, so assemble output line:
if defined FND (
set "NEW=%%~I%_SEPAR%"
setlocal EnableDelayedExpansion
for /F "delims=" %%K in (^""!BUF!!NEW!"^") do (
endlocal
set "BUF=%%~K"
)
)
)
rem // Process 1st line string:
set /A "IDX=0"
for %%I in (%%~F) do (
rem // Compare column index of current item with given column list:
set /A "IDX+=1" & set "FND="
for %%J in (%_COLS2%) do (
setlocal EnableDelayedExpansion
if !IDX! EQU %%J (
endlocal & set "FND=#"
) else endlocal
)
rem // Matching column index encountered, so assemble output line:
if defined FND (
set "NEW=%%~I%_SEPAR%"
setlocal EnableDelayedExpansion
for /F "delims=" %%K in (^""!BUF!!NEW!"^") do (
endlocal
set "BUF=%%~K"
)
)
)
setlocal EnableDelayedExpansion
)
)
rem // Return output line buffer:
echo(!BUF:~1,-1!
endlocal
endlocal
exit /B

Including Double Quotes in Batch Search and Replace script

I have a config file on many remote machines that I need to modify through the use of a batch script. The config file has two lines like this:
1_IP = "10.101.34.216"
2_IP = "10.101.34.214"
I simply need to swap the two IP's, but I'm having a hard time.
Ideally, I would write a simple script to search for 10.101.34.216 and replace it with 10.101.34.214 and vice versa - however, if I accidentally run the script on the remote machines in the future it would just revert to the original.
Therefore, I need to set the search parameter to look for exactly 1_IP = "10.101.34.216" and replace it with exactly 1_IP = "10.101.34.214" and then subsequently an exact search for 2_IP = "10.101.34.214" to be replaced with exactly 2_IP = "10.101.34.216"
I'm currently using the following script, which I found on a separate stackoverflow post.
#echo off
setlocal enableextensions disabledelayedexpansion
set "search=%1"
set "replace=%2"
set "textFile=system.cfg"
for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
set "line=%%i"
setlocal enabledelayedexpansion
set "line=!line:%search%=%replace%!"
>>"%textFile%" echo(!line!
endlocal
)
However, if I set search parameter to
set search= "1_IP = "10.101.34.216" "
It does not work because of the double quotes. I've tried several approaches to escape the double quotes, but I can't seem to get it to work. Any suggestions?
Thank you in advance for your time, patience, and expertise.
Since the current strings behind 1_IP = and 2_IP = do not seem to matter, I would not try to replace them, but I would simply write the desired strings, like this (see all the explanatory rem comments):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=%~dp0system.cfg" & rem // (path/name of file to process)
set "_KEY[1]=1_IP" & rem // (1st key to search for)
set "_KEY[2]=2_IP" & rem // (2nd key to search for)
set "_VAL[1]=10.101.34.214" & rem // (1st value to assign to 1st key)
set "_VAL[2]=10.101.34.216" & rem // (2nd value to assign to 2nd key)
rem // Read specified file and iterate through all (non-empty) lines:
for /F delims^=^ eol^= %%L in ('type "%_FILE%" ^& ^> "%_FILE%" rem/') do (
rem // Write (append) to the specified file:
>> "%_FILE%" (
rem // Split key from value (key must not contain spaces):
for /F "eol== delims== " %%K in ("%%L") do (
rem /* If key is a predefined one return respective value;
rem otherwise return the current line unedited: */
if /I "%%K"=="%_KEY[1]%" (
echo(%%K = "%_VAL[1]%"
) else if /I "%%K"=="%_KEY[2]%" (
echo(%%K = "%_VAL[2]%"
) else (
echo(%%L
)
)
)
)
endlocal
exit /B
If you want to be able to predefine an arbitrary number of keys and values, and to retain empty lines, you may want to use this script:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=%~dp0system.cfg" & rem // (path/name of file to process)
set "_KEY[1]=1_IP" & rem // (1st key to search for)
set "_KEY[2]=2_IP" & rem // (2nd key to search for)
set "_VAL[1]=10.101.34.214" & rem // (1st value to assign to 1st key)
set "_VAL[2]=10.101.34.216" & rem // (2nd value to assign to 2nd key)
rem // Read specified file and iterate through all lines, preceded by line number:
for /F "delims=" %%L in ('findstr /N "^" "%_FILE%" ^& ^> "%_FILE%" rem/') do (
rem // Write (append) to the specified file:
>> "%_FILE%" (
rem // Split line number plus key from value (key must not contain spaces):
for /F "delims== " %%K in ("%%L") do (
rem // Store extracted key and full line, both including line numbers:
set "KEY=%%K" & set "LINE=%%L"
setlocal EnableDelayedExpansion
rem // Clear flag, remove line number from key:
set "FLAG=" & set "KEY=!KEY:*:=!"
rem // Loop through all available keys:
for /F "tokens=2 delims=[]=" %%M in ('2^> nul set _KEY[') do (
rem /* If key is a predefined one return respective value;
rem otherwise set flag to indicate key has been found: */
if /I "!KEY!"=="!_KEY[%%M]!%" (
echo(!KEY! = "!_VAL[%%M]!"
set "FLAG=#"
)
)
rem // Return current line unedited in case flag is not set:
if not defined FLAG echo(!LINE:*:=!
endlocal
)
)
)
endlocal
exit /B
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET "filename1=%sourcedir%\q41578841.txt"
SET "outfile=%destdir%\outfile.txt"
SET "search1=1_IP = \"10.101.34.216\""
SET "search2=2_IP = \"10.101.34.214\""
SET "replace1=1_IP = "10.101.34.214""
SET "replace2=2_IP = "10.101.34.216""
SET "replaced=N"
(
FOR /f "usebackqtokens=1*delims=" %%a IN ("%filename1%") DO (
ECHO(%%a|FINDSTR /x /L /C:"%search1%" >NUL
IF ERRORLEVEL 1 (
ECHO(%%a|FINDSTR /x /L /C:"%search2%" >NUL
IF ERRORLEVEL 1 (ECHO(%%a
) ELSE (SET "replaced=Y"&ECHO(%replace2%
)
) ELSE (SET "replaced=Y"&ECHO(%replace1%
)
)
)>"%outfile%"
IF %replaced%==Y (MOVE "%outfile%" "%filename1%">nul&ECHO made changes
) ELSE (DEL "%outfile%"&echo no changes made
)
GOTO :EOF
You would need to change the settings of sourcedir and destdir to suit your circumstances.
I used a file named q41578841.txt containing your data plus some dummy data for my testing.
%outfile% may be used as a temporary file. its name is not relevant, it just needs to not-exist when the job is run.
read each file line. if the line exactly matches (/x) the search-string (/c: since it may contain spaces, /L literally - /i for case-insensitive omitted) then set errorlevel 0. if neither matches, regurgitate the line, else output the replacement line and flag that the replacement took place.
Finally, either replace the file or delete the dummy output file and report.
It's not pretty, but when is Windows Shell script?...:
#echo off
setlocal
set TEXT_FILE=.\system.cfg
set IP_1=
set IP_2=
for /f "tokens=*" %%L in (%TEXT_FILE%) do call :PROCESS_LINE %%L
echo 1_IP = "%IP_2%"
echo 2_IP = "%IP_1%"
endlocal
goto END
:PROCESS_LINE
set PL_LINE=%*
set PL_LINE=%PL_LINE:"=%
if "%PL_LINE:~0,4%" == "1_IP" set IP_1=%PL_LINE:~7%
if "%PL_LINE:~0,4%" == "2_IP" set IP_2=%PL_LINE:~7%
goto END
:END

cmd command to dump file list in numeric order

Sorry I can't word this correctly but I need a command to create a text file that dumps the file names in numeric order. Here is the command that does what I want but everything that has 1 at the start is bunched, 2, 3 etc.....
(for %i in (*.flv) do #echo file '%i') > file.txt
Most cmd/batch file commands, like dir, sort and set, for instance, do pure alphabetic sorting, so any numeric parts are not treated particularly. For example, string12 appears before string3, because the character 1 appears before 2 with the used way of sorting. To change the behaviour so that alpha-numeric sorting is applied, meaning that string3 appears before string12, you have to write your own code for accomplishing that. The following script does exactly that, by padding every numeric part appearing in strings/file names to a fixed amount of digits, in which case alpha-numeric and alphabetic sort orders match. Below is the code, including explanatory remarks. As you can see, complex code is required to accomplish the task and to make the script secure against all characters that have special meanings to cmd (like SPACE, ,, ;. = as well as ^, &, (, ), %, !):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_TEMPFILE=%TEMP%\%~n0_%RANDOM%.tmp" & rem // (temporary file used for sorting)
set /A "_DIGITS=12" & rem // (number of digits every numeric part is padded for sorting)
rem // Build string containing enough zeros for padding:
set "$PAD=" & setlocal EnableDelayedExpansion
for /L %%D in (1,1,%_DIGITS%) do set "$PAD=!$PAD!0"
endlocal & set "$PAD=%$PAD%"
rem // Prepare temporary file containing data to sort:
> "%_TEMPFILE%" (
rem // Gather and resolve all command line arguments:
for %%I in (%*) do (
rem // Get pure file name:
set "ITEM=%%~nxI"
rem // Extend all numeric parts in file name to certain number of digits:
call :PROCESS AUGM ITEM || >&2 echo ERROR: potential problem sorting "%%~nxI"!
rem // Write extended and original file name into temporary file:
setlocal EnableDelayedExpansion
echo(!AUGM!^|!ITEM!
endlocal
)
)
rem // Return content of temporary file in ascendingly sorted order:
for /F "tokens=2 delims=| eol=|" %%J in ('sort "%_TEMPFILE%"') do (
rem // Simply return each item:
echo(%%J
)
rem // Delete temporary file:
del "%_TEMPFILE%"
endlocal
exit /B
:PROCESS rtn_augmented_string ref_string
rem /* Routine to augment a string so that every numeric part is padded with leading
rem zeros to the left to hold a predefined number of digits: */
setlocal DisableDelayedExpansion
set "#RTN=%~1"
set "#ARG=%~2"
rem // Initialise required variables:
set "COLL="
set "ERRL=0"
setlocal EnableDelayedExpansion
for /F delims^=^ eol^= %%B in (^""!%#ARG%!"^") do (
endlocal
set "PSTR=%%~B"
setlocal EnableDelayedExpansion
)
rem // Entry point for loop to handle one numeric string part:
:REPEAT
rem // Extract the string portions before and after the first numeric part:
for /F "tokens=1,* delims=0123456789 eol=0" %%A in ("+!PSTR!") do (
endlocal
set "PART=%%A"
set "NEXT=%%B"
rem // Determine length of string portion before first numeric part:
call :LENGTH PLEN PART
set /A "PLEN-=1"
setlocal EnableDelayedExpansion
set "PART=!PART:~1!"
rem // Split off string portion before first numeric part from total string:
for %%C in (!PLEN!) do (
if defined PSTR set "PSTR=!PSTR:~%%C!"
)
rem /* Splitt off string portion after first numeric part from remaining string;
rem this is nothing but extracting the first numeric part itself: */
call :SPLIT PNUM NEXT PSTR
rem // Determine the actual length of the numeric part:
call :LENGTH NLEN PNUM
rem // Do the actual padding with leading zeros of the numeric part:
if defined PNUM (
set "PNUM=%$PAD%!PNUM!"
set "PNUM=!PNUM:~-%_DIGITS%!"
)
rem // Store the part after the first numeric part:
for /F delims^=^ eol^= %%C in (^""!NEXT!"^") do (
rem /* Build string with the string portion before the current numeric part
rem and the padded current numeric part itself: */
for /F delims^=^ eol^= %%D in (^""!COLL!!PART!!PNUM!"^") do (
rem // Check whether the predefined number of padding digits is sufficient:
for /F %%E in ("!NLEN!") do (
endlocal
set "PSTR=%%~C"
set "COLL=%%~D"
if %%E GTR %_DIGITS% set "ERRL=1"
setlocal EnableDelayedExpansion
)
)
)
)
rem // Repeat the whole approach while there is still a remaining string portion:
if defined PSTR goto :REPEAT
rem // Return the string with padded numeric parts:
for /F delims^=^ eol^= %%R in (^""!COLL!"^") do (
endlocal
endlocal
set "%#RTN%=%%~R"
exit /B %ERRL%
)
exit /B
:SPLIT rtn_left_string ref_split_char val_string
rem /* Routine to split a string at the first occurrence of a certain character and to
rem return the portion before it: */
setlocal DisableDelayedExpansion
set "#RTN=%~1"
set "#CHR=%~2"
set "#ARG=%~3"
rem // Initialise required variables:
setlocal EnableDelayedExpansion
set "CHAR= " & if defined %#CHR% set "CHAR=!%#CHR%:~,1!"
if "!CHAR!"=="<" (set "PREF=>") else (set "PREF=<")
rem // Check whether a split character is defined:
if defined %#CHR% (
rem /* Split character available, so split off first occurrence and everything after
rem from the original string: */
for /F eol^=^%CHAR%^ delims^=^%CHAR% %%C in ("%PREF%!%#ARG%!") do (
endlocal
set "%#RTN%=%%C"
setlocal EnableDelayedExpansion
set "%#RTN%=!%#RTN%:~1!"
)
) else (
rem // No split character defined, so do not split off anything:
set "%#RTN%=!%#ARG%!"
)
rem // Return the resulting string:
for /F delims^=^ eol^= %%R in (^""!%#RTN%!"^") do (
endlocal
endlocal
set "%#RTN%=%%~R"
)
exit /B
:LENGTH rtn_length ref_string
rem /* Routine to determine the length of a given string: */
setlocal DisableDelayedExpansion
set "#RTN=%~1"
set "#ARG=%~2"
setlocal EnableDelayedExpansion
rem // Check whether a string is provided:
if defined %#ARG% (
rem // String is available, so calculate its length:
set /A "%#RTN%=1"
for %%A in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if not "!%#ARG%:~%%A!"=="" (
set /A "%#RTN%+=%%A"
set "%#ARG%=!%#ARG%:~%%A!"
)
)
) else (
rem // String is empty, so length is zero:
set /A "%#RTN%=0"
)
rem // Return the computed length:
for /F %%R in ("!%#RTN%!") do (
endlocal
endlocal
set "%#RTN%=%%R"
)
exit /B
Provide (a) file pattern(s) as (a) command line argument(s), like this, for example (supposing the batch file is saved as sort-alpha-num.bat):
sort-alpha-num.bat "*.flv"
To store the resulting sorted list in a text file called file.txt, use this:
sort-alpha-num.bat "*.flv" > "file.txt"
In case an error message like ERROR: potential problem sorting "1000000000000.flv"! appears, increase the number of digits on top of the script (see line set /A "_DIGITS=12").
Since you didn't give us more information about how your output file should be ?
#echo off
set "folder=%userprofile%\Desktop\*.flv"
set "listfiles=listfiles.txt"
If Exist %listfiles% Del %listfiles%
setLocal EnableDelayedExpansion
Rem Populate the array with existent files in this folder
for /f "tokens=* delims= " %%a in ('Dir /s /b /a:-d "%folder%"') do (
set /a Count+=1
set "File[!Count!]=%%~na"
)
::******************************************************************
:Display_Files
cls & color 0B
echo(
For /L %%i in (1,1,%Count%) do (
echo %%i - !File[%%i]!
)
echo(
(
rem to save result into logfile
For /L %%i in (1,1,%Count%) do (
echo %%i - !File[%%i]!
)
)>> %listfiles%
echo Hit any key to open %listfiles% :
Pause>nul & Start "" %listfiles%

Resources