New CSV by combining 2 csv files - batch-file

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

Related

How to parse multi-line value from a csv in batch

I am writing a batch script that I need to parse text out of a .csv and have ran into a roadblock:
I have a for-loop set up to grab data from each line (this works fine) but I end up needing a value that is separated by multiple lines. For example (I placed what I want to be considered a single entry in parenthesis for context):
(data I need,flag_for_which_process_to_run,dontcare,"data I need
data continued
data continued
this could continue for any number of lines",dontcare,dontcare,dontcare,dontcare)
(repeat)
Is there any way to get a batch script to parse this out without breaking the for loop? If it's helpful, the data in %%d is encased in double quotes. Code is below, the section I am referring to is the second if inside the for loop.
SETLOCAL EnableDelayedExpansion
for /f "tokens=1,2,3,4 delims=," %%a in (sample.csv) do (
REM Skip if %%b is not flag1
if "%%b"=="flag1" (
.
.
.
)
REM Skip if %%b is not otherflag
if "%%b"=="otherflag" (
REM Set the %%a variable
set device=%%a
echo "%%d"> output\tmp\temp.txt
)
)
Given that the first three tokens/values are unquoted (so they cannot contain quotation marks or commas on their own) and the whole CSV file does not contain escape or back-space characters, the following script, when the CSV file is provided as a command line argument, should extract the values you are interested in (it just echoes them out):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=%~1" & rem // (CSV file; `%~1` is first command line argument)
rem // Get carriage-return character:
for /F %%C in ('copy /Z "%~0" nul') do set "_CR=%%C"
rem // Get line-feed character:
(set ^"_LF=^
%= blank line =%
^")
rem // Get escape and back-space characters:
for /F "tokens=1,2" %%E in ('prompt $E$S$H ^& for %%Z in ^(.^) do rem/') do set "_ESC=%%E" & set "_BS=%%F"
set "CONT="
rem // Read CSV file line by line:
for /F usebackq^ delims^=^ eol^= %%L in ("%_FILE%") do (
rem // Branch for normal lines:
if not defined CONT (
rem // Get relevant tokens/values:
for /F "tokens=1-3* delims=, eol=," %%A in ("%%L") do (
set "DEVICE=%%A" & set "FLAG=%%B" & set "LINE=%%D"
if not "%%D"=="%%~D" (
rem // Fourth token begins with a `"`, hence remove it and enter branch for continued lines then:
for /F delims^=^ eol^= %%E in ("%%D"^") do set "LINE=%%~E"
set "DATA=" & set "CONT=#"
) else (
rem // Fourth token does not begin with a '"', hence it cannot be continued:
for /F "delims=, eol=," %%E in ("%%D") do (
rem // Do something with the data, like echoing:
echo/
echo FLAG=%%B
echo DEVICE=%%A
echo DATA=%%E
)
)
)
) else set "LINE=%%L"
rem // Branch for continued lines:
if defined CONT (
setlocal EnableDelayedExpansion
rem // Temporarily replace escaped (doubled) `"` with back-space character:
set "LINE=!LINE:""=%_BS%!"
rem // Collect continued data with line-breaks replaced by escape characters:
for /F delims^=^"^ eol^=^" %%D in ("!DATA!%_ESC%!LINE!") do endlocal & set "DATA=%%D"
setlocal EnableDelayedExpansion
if not "!LINE!"=="!LINE:"=!^" (
rem /* There is a single `"` (plus a `,`), which is taken as the end of the continued fourth token;
rem hence replacing back line-breaks and (unescaped) `"`: */
set "DATA=!DATA:*%_ESC%=!" & set "DATA=!DATA:%_BS%="!^"
for %%E in ("!_CR!!_LF!") do set "DATA=!DATA:%_ESC%=%%~E!"
rem // Do something with the data, like echoing:
echo/
echo FLAG=!FLAG!
echo DEVICE=!DEVICE!
echo DATA=!DATA!
endlocal
set "CONT="
) else endlocal
)
)
endlocal
exit /B

adding a new column to csv and populate the values alternatives

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"

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%

use batch file to read and write version number into a file

I'm trying to write a batch file to increment version number every time i run it, but I'm confuse about "for /f" and the behaviour of the batch file when I test it by using command prompt. Please help me with this.
here's my batch file
for /f "tokens=2,3 " %%i in (version.h) do (
set /a number=%%j+1
echo %%i
echo %%j
echo %number%
if %%i==REVISION (
echo line1
echo #define %%i "%number%" >> temp.file
) else (
echo line2
echo #define %%i %%j >> temp.file
)
)
del /q version.h
ren temp.file version.h
and here's my version.h
#define MAJOR "1"
#define MINOR "0"
#define REVISION "242"
The batch file can only produce correct result at the first run(#define REVISION "243"), and has a weird result at the second run(#define REVISION "0"). The third run's result is correct("#define REVISION "244"), but the forth run it goes weird again(#define REVISION "1"), and so on.
It seems that I didn't parse the correct string so I cannot have correct result every time.
I typed "for /?" in the command prompt and read the help message, but still cannot understand it, please help me with this. Any reply would be appreciate!
The following script does what you want and preserves empty lines and special characters present in the target file. There is no temporary file involved, the file modification is accomplished in-place.
So here is the code -- reference the explanatory remarks for how it works:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "FILE=%~1" & rem // (provide the target file as command line argument)
set "DIRECTIVE=#define" & rem // (name of the directive to search)
set "DEFINITION=REVISION" & rem // (name of the definition to search)
set "CASESENS=" & rem // (set to non-empty for case-sensitive searches)
set "QUOTED="^" & rem // (set to non-empty for quoting returned number)
rem // Resolve arguments and options:
if not defined FILE ((>&2 echo ERROR: no file specified!) & exit /B 1)
if defined CASESENS (set "CASESENS=") else (set "CASESENS=/I")
if defined QUOTED (set "QUOTED="^") else (set "QUOTED=")
rem // Loop through all lines in the target file:
setlocal EnableDelayedExpansion
for /F delims^=^ eol^= %%L in ('
rem/ /* Prefix lines with line numbers to not lose empty ones; ^& ^
rem/ after having read file, deplete its entire content: */ ^& ^
findstr /N /R "^^" "!FILE!" ^& ^> "!FILE!" break
') do (
endlocal
set "FLAG="
set "LINE=%%L"
rem // Split line into three tokens:
for /F "tokens=1-3 eol= " %%I in ("%%L") do (
set "FIELD1=%%I"
set "FIELD2=%%J"
set "NUMBER=%%~K"
setlocal EnableDelayedExpansion
rem // Check first token for matching directive name:
if %CASESENS% "!FIELD1:*:=!"=="!DIRECTIVE!" (
rem // Check second token for matching definition name:
if %CASESENS% "!FIELD2!"=="!DEFINITION!" (
endlocal
rem // Increment number of third token:
set /A "NUMBER+=1"
set "FLAG=#"
setlocal EnableDelayedExpansion
)
)
endlocal
)
setlocal EnableDelayedExpansion
rem // Write output line into target file:
>> "!FILE!" (
rem // Check whether dirctive and definition matched:
if defined FLAG (
rem // Match found, so write new line with incremented number:
echo(!DIRECTIVE! !DEFINITION! %QUOTED%!NUMBER!%QUOTED%
) else (
rem // No match found, so write original line:
echo(!LINE:*:=!
)
)
)
endlocal
endlocal
exit /B

Resources