How to substitute a string in batch script? - batch-file

My arqtext.txt has the following dataset:
A,B,C,
(123 or 456) and (789 or 012),1,5,
(456 or 654) and (423 or 947),3,6,
(283 or 335) and (288 or 552),2,56,
I want to change the 1st column of the last 3 rows to a new string set in the script, with the result like:
A,B,C,
roi1,1,5,
roi2,3,6,
roi3,2,56,
But my code only output the header "A,B,C,":
#echo off
setlocal EnableDelayedExpansion EnableExtensions
set roi1="(123 or 456) and (789 or 012)"
set roi2="(456 or 654) and (423 or 947)"
set roi3="(283 or 335) and (288 or 552)"
set /p "header="<"arqtext.txt"
echo %header%>arqtextnovo.txt
for /f "skip=1 tokens=1,* delims=," %%a in ("arqtext.txt") do (
if %%a=="roi1" (
echo roi1,%%b>>arqtextnovo.txt
)
if %%a=="roi2" (
echo roi2,%%b>>arqtextnovo.txt
)
if %%a=="roi3" (
echo roi3,%%b>>arqtextnovo.txt
)
)
rem EXIT /B
pause>nul

This is the way I would do it:
#echo off
setlocal EnableDelayedExpansion
set "roi[(123 or 456) and (789 or 012)]=1"
set "roi[(456 or 654) and (423 or 947)]=2"
set "roi[(283 or 335) and (288 or 552)]=3"
set /P "header=" < "arqtext.txt"
> arqtextnovo.txt (
echo %header%
for /f "usebackq skip=1 tokens=1,* delims=," %%a in ("arqtext.txt") do (
echo roi!roi[%%a]!,%%b
)
)
rem EXIT /B
pause>nul
The for /F command requires "usebackq" option if the filename is enclosed in quotes. Otherwise, it process the literal string enclosed in quotes.
It is more efficient to (redirect the whole output) > to a file, instead of append >> every new line. This also avoid the problems of redirect lines that ends in numbers.
If you have several values and want to select a result value based on the first one, it is much simpler and more efficient to use an array instead of test each individual value.
Let's suppose this method:
set /P "selector=Enter selector: "
if "%selector%" equ "nine" set result=9
if "%selector%" equ "seven" set result=7
if "%selector%" equ "five" set result=5
Instead, you may define an array called "value":
set "value[nine]=9"
set "value[seven]=7"
set "value[five]=5"
... and then directly get the result value this way:
set "result=!value[%selector%]!"
The same method is used in this code. However, you have not specified what happen if the input value is not one of the array elements.
For a further description on array management in Batch files, see this answer

Related

Rename value in column via batch

I have a tab delimited txt file and I am trying to find the value 0 in the last column in every line then rename that value from 0 to K.This is the code I have come up with so far but I can't get the values to change. What am I doing wrong here?
ECHO ON
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=E:\psexport"
SET "destdir=E:\psexport"
SET "filename1=%sourcedir%\nk3.txt"
SET "outfile=%destdir%\nk4.txt"
(
FOR /f * IN ("%filename1%") DO (
SET "line=*"
SET "line=!line:0=K"
ECHO !line!
)
)>"%outfile%"
GOTO :EOF
`
You are using bang where I would expect to see percentage. Also, I'm not sure about that second set statement.
See my code example below. In the echo command I am calling line, with the variable substitution of replace 0 with K. It's wrapped in percentages because that whole expression is the variable we want to echo.
ECHO ON
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=E:\psexport"
SET "destdir=E:\psexport"
SET "filename1=%sourcedir%\nk3.txt"
SET "outfile=%destdir%\nk4.txt"
(
FOR /f * IN ("%filename1%") DO (
SET "line=*"
ECHO %line:0=K%
)
)>"%outfile%"
GOTO :EOF
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=E:\psexport"
SET "destdir=E:\psexport"
SET "filename1=%sourcedir%\nk3.txt"
SET "outfile=%destdir%\nk4.txt"
(
FOR /f "usebackq delims=" %%a IN ("%filename1%") DO (
SET "line=%%a"
IF "!line:~-2!"==" 0" (
ECHO !line:~0,-2! K
) ELSE (
ECHO %%a
)
)
)>"%outfile%"
GOTO :EOF
The for syntax requires a metavariable - I've used %%a - which is alphabetical and case-sensitive.
The modifier usebackq is used because the name of the file being read is quoted.
delims= means no delimiters, so the entire file line is delivered to %%a.
To use substringing, we need to transfer the value to a standard environment variable, then test that the last two characters of the line read are 0 so that 10 does not get modified. If the test is true, echo the line from the beginning (character 0) through to the end - 2 characters and append K, otherwise, regurgitate the original line.
The set syntax you were using replaces every 0 with K.

Batch: 'FOR' cmd works partially, 'skip=' option not working

I want to create a script that sets specific values, then writes each value into a new line of a text document. After that it should read the document and set new values to a specified line of the text document, then echo those out.
I have tried different values for "skip=#" which didn't change anything. When I tried to not use the "skip=0" option in the first FOR and that makes the batch echo out "Value three" for all values. (Quick edit: I've used this website for information on it so far.)
#ECHO OFF
REM Setting values
SET #valueone=Value one
SET #valuetwo=Value two
SET #valuethree=Value three
REM Saving values
IF EXIST "values.txt" DEL "values.txt"
echo %#valueone% >values.txt
echo %#valuetwo% >>values.txt
echo %#valuethree% >>values.txt
REM Reading values again and echoing them at at the same time.
REM This was separated (first reading then echoing) but it didn't change anything.
FOR /F "skip=0 delims=" %%i IN (values.txt) DO SET #valueonefinal=%%i
echo Value number one:
echo %#valueonefinal%
echo.
FOR /F "skip=1 delims=" %%i IN (values.txt) DO SET #valuetwofinal=%%i
echo Value number two:
echo %#valuetwofinal%
echo.
FOR /F "skip=2 delims=" %%i IN (values.txt) DO SET #valuethreefinal=%%i
echo Value number three:
echo %#valuethreefinal%
pause
Expected output in the console:
Value number one:
Value one
Value number two:
Value two
Value number three:
Value three
Actual output:
delims=" was unexpected at this time.
Value number one:
ECHO is off.
Value number two:
Value three
Value number three:
Value three
I'm not that experienced but I suspect that I may be doing the "skip=#" part wrong. Any help with this is greatly apprechiated!
The option skip=0 is not accepted by the for /F command, the specified number must be in the range from 1 to 231 − 1. To skip no lines just do not provide the skip option at all.
You seem to try to assign the text of a certain line to a variable (for instance, the third one):
FOR /F "skip=2 delims=" %%i IN (values.txt) DO SET #valuethreefinal=%%i
Well, this actually assigns the content of the last line to the variable, because the set command in the body of the loop is executed for all but the skipped lines. More precisely said, the for /F loop iterates over all non-empty lines which do not begin with ; which is the default character of the eol option.
To actually assign the third line to the variable you need to change the code:
rem // Ensure that the variable is initially unset somewhere before:
set "#valuethreefinal="
rem // As soon as the variable is set the `if` condition is no longer going to be fulfilled:
for /F "usebackq skip=2 delims=" %%i in ("values.txt") do if not defined #valuethreefinal set "#valuethreefinal=%%i"
This does not necessarily assign the third line to the variable, it actually assigns the text of the first line after the (two) skipped ones that is not empty and does not begin with ; (remember the eol character).
The usebackq option allows to put quotation marks around the file name. This is not necessary in your situation, but it is when a file name contains SPACEs or other special characters.
I used the undocumented quoted set syntax here because this is safer than the unquoted one, particularly when it comes to special characters and also to avoid unintended trailing white-spaces.
To disable the eol character you could use the undocumented unquoted option string syntax:
for /F usebackq^ skip^=2^ delims^=^ eol^= %%i in ("values.txt") do if not defined #valuethreefinal set "#valuethreefinal=%%i"
As you can see the SPACEs and =-signs are escaped by the caret symbol ^ in order to treat the whole option string as a unit.
This still skips over empty lines though. To prevent this take a loop at this thread: preserve empty lines in a text file while using batch for /f.
Since you want to capture more than a single line you could extend the code to the following:
set "#valueonefinal=" & set "#valuethreefinal=" & set "#valuethreefinal="
for /F usebackq^ delims^=^ eol^= %%i in ("values.txt") do (
if not defined #valueonefinal (
set "#valueonefinal=%%i"
) else (
if not defined #valuetwofinal (
set "#valuetwofinal=%%i"
) else (
if not defined #valuethreefinal (
set "#valuethreefinal=%%i"
)
)
)
)
This can be compressed to:
set "#valueonefinal=" & set "#valuethreefinal=" & set "#valuethreefinal="
for /F usebackq^ delims^=^ eol^= %%i in ("values.txt") do (
if not defined #valueonefinal (
set "#valueonefinal=%%i"
) else if not defined #valuetwofinal (
set "#valuetwofinal=%%i"
) else if not defined #valuethreefinal (
set "#valuethreefinal=%%i"
)
)
A more flexible method is to use pseudo-arrays:
rem // Initialise an index counter:
set /A "INDEX=0"
rem // Assign every line to an element of a pseudo-array:
for /F usebackq^ delims^=^ eol^= %%i in ("values.txt") do (
rem // Increment the index counter:
set /A "INDEX+=1"
rem // Assign the current line to a pseudo-array element:
call set "#valuefinal[%%INDEX%%]=%%i"
)
The (non-empty) lines of the file value.txt are now assigned to variables called #valuefinal[1], #valuefinal[2], #valuefinal[3], etc. (there is no concept of arrays in batch scripting, the variables are exactly the same as yours, #valueonefinal, etc., that is why I use the term "pseudo").
The call command is used here in order to be able to write and read the variable INDEX within the same block of code; just using set "#valuefinal[%INDEX%]=%%i" would result in assigning and therefore overwriting the variable #valuefinal[0] in every loop iteration.
Your problem is that you are parsing the File from Top to bottom, and skipping the First value, what you don't realize is that FOR will set the value to the LAST item it found. This means that the script as written can only ever return the last item in the values file.
To deal with this you could:
Break the loop on the first match and return that result.
Remove values as they are matched
I like to Break the loop.
First let me make you code a little more streamlined so we can re-write it multiple times to show each
This is going to work exactly as your existing code but now we can easily add more values and loop them in a quick go.
Your Original Code Refactored:
#( SETLOCAL EnableDelayedExpansion
ECHO OFF
SET "_ValuesFile=%~dp0values.txt"
REM Remove Old Values File
DEL /F /Q "!_ValuesFile!" >NUL 2>NUL
REM Saving values
FOR %%A IN (one two three) DO (
ECHO.Value %%A>>"!_ValuesFile!" )
)
CALL :Main
( PAUSE
ENDLOCAL
EXIT /B 0
)
:Main
FOR /L %%L IN (0,1,2) DO (
CALL SET /A "_Value=%%L + 1"
ECHO.&ECHO.------ Iteration: %%L ------&ECHO.Value number !_Value!:
IF %%L EQU 0 ( SET "_ForOptions=tokens=*" ) ELSE (
SET "_ForOptions=Skip=%%L tokens=*" )
CALL :Loop %%L
)
GOTO :EOF
:Loop
FOR /F "%_ForOptions%" %%i IN (' type "%_ValuesFile%"
') DO ( CALL SET "#value%_Value%final=%%i" )
ECHO.!#value%_Value%final!
GOTO :EOF
* Break the Loop on the First Match:
#( SETLOCAL EnableDelayedExpansion
ECHO OFF
SET "_ValuesFile=%~dp0values.txt"
REM Remove Old Values File
DEL /F /Q "!_ValuesFile!" >NUL 2>NUL
REM Saving values
FOR %%A IN (one two three) DO (
ECHO.Value %%A>>"!_ValuesFile!" )
)
CALL :Main
( PAUSE
ENDLOCAL
EXIT /B 0
)
:Main
FOR /L %%L IN (0,1,2) DO (
CALL SET /A "_Value=%%L + 1"
ECHO.&ECHO.------ Iteration: %%L ------&ECHO.Value number !_Value!:
IF %%L EQU 0 ( SET "_ForOptions=tokens=*" ) ELSE (
SET "_ForOptions=Skip=%%L tokens=*" )
CALL :Loop %%L
)
ECHO.&ECHO.------ Final Values After %%L Iterations: ------
SET #value
GOTO :EOF
:Loop
FOR /F "Tokens=*" %%A IN ('
CMD /C "FOR /F %_ForOptions% %%i IN (' type "%_ValuesFile%" ') DO #(ECHO.%%i&exit /b)"
') DO #(
SET "#value%_Value%final=%%~A"
)
ECHO.!#value%_Value%final!
GOTO :EOF
Example Output from Break the Loop Version:
Y:\>C:\Admin\S-O_Value-Checker_v2.cmd
------ Iteration: 0 ------
Value number 1:
Value one
------ Iteration: 1 ------
Value number 2:
Value two
------ Iteration: 2 ------
Value number 3:
Value three
------ Final Values After %L Iterations: ------
#value1final=Value one
#value2final=Value two
#value3final=Value three
Press any key to continue . . .

Batch/cmd - subroutine does not "return" array via parameter

In the following script I want to pass a string via variable and the variable name for an array which should contain substrings to a subroutine.
The subroutine puts substrings of the passed string into an array/list which then should get "returned" by setting it as the value of the 2. passed parameter.
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET testString=Hello World
REM Pass testString and substrings to subroutine
CALL :get_substrings testString substrings
REM For testing. Echo substrings. DOESN'T WORK. substrings is empty!
FOR /L %%s IN (0,1,2) DO (
ECHO !substrings[%%s]!
)
ENDLOCAL
EXIT /B 0
:get_substrings
SETLOCAL ENABLEDELAYEDEXPANSION
SET "string=!%~1!"
REM Alternative approach: Make a connection to %2 rightaway
REM SET "substrings=!%~2!"
REM Process string: Put substrings into indexed array. This works as expected!
FOR /L %%s IN (0,1,2) DO (
SET substrings[%%s]=!string:~0,5!
SET string=!string:~5!
)
REM For testing. Echo the substrings. Works as expected!
FOR /L %%s IN (0,1,2) DO (
ECHO !substrings[%%s]!
)
REM For alternative approach
REM ENDLOCAL
REM End the local the set 2.param = substringsArray
ENDLOCAL & SET %2=%substrings%
EXIT /B 0
Processing the string by creating a array with substrings in the subroutine works as expected. But setting 2. parameters value and keeping the value after subroutine doesn't work...
Notes: The processing of the string is just a dummy. The real process is slightly different but the core with the substrings array is the same. The script is executable right away.
So, how can I get the value substrings back?
This does what you want:
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET testString=Hello World
REM Pass testString and substrings to subroutine
CALL :get_substrings testString substrings
REM For testing. Echo substrings.
FOR /L %%s IN (0,1,2) DO (
ECHO !substrings[%%s]!
)
ENDLOCAL
EXIT /B 0
:get_substrings
SETLOCAL ENABLEDELAYEDEXPANSION
SET "string=!%~1!"
REM Process string: Put substrings into indexed array. This works as expected!
FOR /L %%s IN (0,1,2) DO (
SET substrings[%%s]=!string:~0,5!
SET string=!string:~5!
)
REM For testing. Echo the substrings. Works as expected!
FOR /L %%s IN (0,1,2) DO (
ECHO !substrings[%%s]!
)
REM End the local the set 2.param = substringsArray
set SubEnviron=1
for /F "tokens=2* delims=[]=" %%a in ('set substrings[') do (
if defined SubEnviron ENDLOCAL
set "%2[%%a]=%%b"
)
EXIT /B 0
I wasn't able to understand your counting of characters so here's how I'd probably do it:
#Echo Off
SetLocal EnableDelayedExpansion
Set "TestString=Hello World"
For /F "Delims==" %%A In ('Set SubString[ 2^>Nul') Do Set "%%A="
Set "i=1"
Set "SubString[%i%]=%TestString: ="&Set/A i+=1&Set "SubString[!i!]=%"
Set SubString[
Pause
Example Output:
SubString[1]=Hello
SubString[2]=World
Press any key to continue . . .
For the purposes of testing you probably don't need the For loop, its purpose is to ensure there are no existing variables whose name begins with SubString[
Edit
This uses three parameters:
The string to cut%string%
A number of how long each substring should be%chrnum%
The substring parameter%strvar%
#Echo Off
SetLocal EnableDelayedExpansion
Set "string=montuewedthufrisatsun"
Set "chrnum=3"
Set "strvar=substring"
Set "i=1"
Set "_=%string%"
:Loop
Set "!strvar![%i%]=!_:~,%chrnum%!"
If "!_:~%chrnum%!"=="" GoTo Write
Set "_=!_:~%chrnum%!"
Set /A i+=1
GoTo Loop
:Write
Set !strvar![ 2>Nul
Pause
Yes, I fully understand that you are not going to change this code to PowerShell. But, it might be worth considering for the next time given how easy it is. get_substrings is a lambda.
PS C:\src\t\selarr> type .\lamb002.ps1
$teststring = 'hello cruel world'
$get_substrings = { param($t) foreach ($s in $t.split()) { $s.Substring(0,4) } }
$a = & $get_substrings $teststring
$a.length
$a[0]
$a[1]
$a[2]
PS C:\src\t\selarr> .\lamb002.ps1
3
hell
crue
worl

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

Batch script How to add space on a numerical variable

First of approaches, excuse me if I do not express myself well in English.
I'm debutante in batch and I need help to make a script
I articles.txt retrieves a document in which there are many lines.
some lines of my document
"T0047" ;"Tuyau 1km";"Marque2";"jardinage";"75 000";"promo"
"T00747";"Tuyau 1m";Marque2";"jardinage";"30 000";"promo"
First, I have to remove the quotation marks in the file.
It is done with:
#echo off
setlocal enabledelayedexpansion
for /F "delims=" %%a in (articles.txt) do (
set a=%%a
set a=!a:"=!
echo !a!
echo !a! >>resultat.txt
)
the result
T0047 ;Tuyau 1km;Marque2;jardinage;75 000;promo
T00747;Tuyau 1m;Marque2;jardinage;30 000;promo
Then I have to perform a multiplication on a column.
For this, I have the problem that if the space is not so mutiplication realize I made a script that removes spaces.
#echo off
setlocal enabledelayedexpansion
for /F "delims=; tokens=1-8" %%a in (resultat.txt) do (
set a=%%e
set a=!a: =!
echo %%a;%%b;%%c;%%d;!a!;%%f;%%g;%%h
echo %%a;%%b;%%c;%%d;!a!;%%f;%%g;%%h >>resultat2.txt
)
the result
T0047 ;Tuyau 1km;Marque2;jardinage;75000;promo
T00747;Tuyau 1m;Marque2;jardinage;30000;promo
Then I made my multiplication.
#echo off
setlocal enabledelayedexpansion
for /F "delims=; tokens=1-8" %%a in (resultat2.txt) do (
set a=%%e
:: set /a a=!a!/0.6
set /a a=!a!*16666/10000
echo %%a;%%b;%%c;%%d;!a!;%%f;%%g;%%h
echo %%a;%%b;%%c;%%d;!a!;%%f;%%g;%%h >>resultat3.txt
)
the result
T0047 ;Tuyau 1km;Marque2;jardinage;124995;promo
T00747;Tuyau 1m;Marque2;jardinage;49998;promo
Now, i add some text just after the first colomn
set champ2=MAGASIN_1;T
for /F "delims=; tokens=1,*" %%a in (resultat3.txt) do (
echo %%a;%champ2%;%%b
echo %%a;%champ2%;%%b >>resultat_final.txt
)
The actual result is:
T0047 ;MAGASIN_1;T;Tuyau 1km;Marque2;jardinage;124995;promo
T00747;MAGASIN_1;T;Tuyau 1m;Marque2;jardinage;49998;promo
Now I would add a space so that the figure is more readable.
T0047 ;MAGASIN_1;T;Tuyau 1km;Marque2;jardinage;124 995;promo
T00747;MAGASIN_1;T;Tuyau 1m;Marque2;jardinage;49 998;promo
This is the way I would do it:
#echo off
setlocal EnableDelayedExpansion
for /F "delims=" %%A in (articles.txt) do (
set "a=%%A"
set a=!a:"=!
for /F "delims=; tokens=1-8" %%a in ("!a!") do (
set /A "g1=%%g*16666/10000"
set "g2="
for /L %%i in (1,1,3) do if defined g1 (
set "g2= !g1:~-3!!g2!"
set "g1=!g1:~0,-3!
)
echo %%a;%%b;%%c;%%d;%%e;%%f;!g2:~1!;%%h
echo %%a;%%b;%%c;%%d;%%e;%%f;!g2:~1!;%%h >> result.txt
)
)
articles.txt:
"T0047" ;"MAGASIN_1";"T";"Tuyau 1km";"Marque2";"jardinage";"75000";"promo"
"T00747";"MAGASIN_1";"T";"Tuyau 1m";Marque2";"jardinage";"30000";"promo"
result.txt:
T0047 ;MAGASIN_1;T;Tuyau 1km;Marque2;jardinage;124 995;promo
T00747;MAGASIN_1;T;Tuyau 1m;Marque2;jardinage;49 998;promo
Your program is good. Some tips:
Don't divide by a power of 10. Instead, remove the fractional part if you don't want it. Use *= . And to get the space in the number:
#echo off
set x=75000
set /a x *= 16666
set x=%x:~0,-4%
echo %x:~0,-3% %x:~-3%
I'll respond only to the multiplication section.
I can see nothing in your code that can possibly generte the two extra columns ;MAGASIN_1;Tand consequently, the target field 75000 and 30000 are in %%g, not %%e.
Comment : Do not use the "broken label" comment form ::comment within a block statement (a parenthesised series of statements) because it can terminate the block prematurely. Always use rem with a block.
So - modified code working on %%g
set a=%%g
rem set /a a=!a!/0.6
REM set /a a=!a!*16666/10000
set /a a=!a!*10/6
SET "a= !a:~-9,-6! !a:~-6,-3! !a:~-3!"
FOR /f "tokens=*" %%q IN ("!a!") DO SET "a=%%q"
echo %%a;%%b;%%c;%%d;%%e;%%f;!a!;%%h
Reason: Batch has a signed-32-bit limit, so if the source field is >~120000 then your calculation will generate a negative number (try 130000 for example) The revised calculation is more accurate and since intermediate results are less likely to exceed 2**31 can cope with larger values in the %%g field.
The set following the calculation changes the numeric value in a to
space(millions)space(thousands)space(units)
(The syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned. set /a can safely be used "quoteless".)
The for /f "tokens=*"... statement simply removes leading spaces from the value of a.
With the explanatin of the two additional columns, This revision should solve the "add-spaces" problem:
set a=%%e
rem set /a a=!a!/0.6
REM set /a a=!a!*16666/10000
set /a a=!a!*10/6
SET "a= !a:~-9,-6! !a:~-6,-3! !a:~-3!"
FOR /f "tokens=*" %%q IN ("!a!") DO SET "a=%%q"
echo %%a;%%b;%%c;%%d;!a!;%%f;%%g;%%h
however, if you want to skip the last step (insertion of 2 extra fields) then insert this line before the for line in the "multiplication" batch
set champ2=MAGASIN_1;T
and change the echo line in that batch to
echo %%a;%champ2%;%%b;%%c;%%d;!a!;%%f;%%g;%%h
Since you have a semicolon-delimited list of values where each item is enclosed within quotation marks, I would go for a standard for to get the items of each line and remove the enclosing quotation marks. The great advantage of this method is that it really cares about the quotation marks, so the list items may even contain semicolons on their own. The only disadvantage is that question marks and asterisks are not allowed in any of the list items:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Redirect all data to output file "resultat.txt" at once:
> "resultat.txt" (
rem Loop through all (non-empty) lines of input file "articles.txt":
for /F "usebackq delims=" %%L in ("articles.txt") do (
rem Reset list collector and loop index:
set "LIST="
set /A "INDEX=0"
rem Loop through the list items of the current line:
for %%I in (%%L) do (
rem Apply current list item with `""` removed, increment loop index:
set "ITEM=%%~I"
set /A "INDEX+=1"
rem Do numeric calculation for a certain list item:
setlocal EnableDelayedExpansion
if !INDEX! EQU 5 (
rem Convert item to a number, avoid error messages:
2> nul set /A "CALC=!ITEM!"
rem Do calculation with rounding (for negative and positive numbers):
if !CALC! LSS 0 (
set /A "CALC=(!CALC!*10-6/2)/6"
) else (
set /A "CALC=(!CALC!*10+6/2)/6"
)
rem Insert thousands separators (space) between every third digit:
set "CALC=!CALC:~-12,-9! !CALC:~-9,-6! !CALC:~-6,-3! !CALC:~-3!"
for /F "tokens=*" %%N in ("!CALC!") do (
set "ITEM=%%N"
)
)
rem Append separator (semicolon) and current item to list:
for /F delims^=^ eol^= %%S in ("!LIST!;!ITEM!") do (
endlocal
set "LIST=%%S"
)
)
rem Return built list, remove superfluous leading separator (`;`):
setlocal EnableDelayedExpansion
echo(!LIST:~1!
endlocal
)
)
endlocal
exit /B
The calculation herein incorporates rounding to the nearest integer, which works even for negative input numbers.
The newly generated list is stored into the new file resultat.txt.

Resources