I have a batch file which reads and set values from a text file.
But the text file contains tabs for different variables
Batch file command:
for /f "tokens=* delims=<TAB>" %%x in (input.txt) do set %%x
Text File(input.txt):
a=one b=two c=three d=four
But the variables are not being set properly.
Two points here:
The default delims= value include the space and Tab as delimiters, so you don't have to include a delims= option, unless you want to ignore the spaces as delimiters!
Your tokens=* option define one token letter in your for command (%%x in this case) that contain all tokens in the line. If you want to get four tokens you need to specify tokens=1-4, start the tokens-letter with another one, and process each token accordingly:
.
for /f "tokens=1-4" %%a in (input.txt) do (
set "%%a" & set "%%b" & set "%%c" & set "%%d"
)
this works even, if your textfile has several lines (with different / unknown number of tokens):
#echo off
setlocal enabledelayedexpansion
for /f "tokens=*" %%i in (input.txt) do (
set line=%%i
for %%x in ("!line: =","!") do set %%x
)
the trick is: by enclosing a string in quotes, delimiter chars are not treated as delimiters any more. So adding a quote at the beginning and end of the string AND replacing each TAB with "quote comma quote" changes the string to:
"a=one","b=two","c=three","d=four"
which can happily be processed with for %%x ...
Related
I am trying to extract the values from the third field of a file which has data records.
The fields are separated by vertical bar characters:
9001||10454145||60|60
9001|234467|10454145||60|60
9001|234457|10454145||60|60
Command is -
for /f "tokens=3 delims=|" %%A IN ('Findstr /i "9001" .\itemloc\%%~nf.dat') do (
echo %%A >> log.txt
)
But the output I am getting is
60
10454145
10454145
The empty fields are messing up my output. Any suggestions how to make the for token work with empty fields in the record?
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following settings for the directories and filenames are names
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "destdir=u:\your results"
SET "filename1=%sourcedir%\q75199035.txt"
SET "outfile=%destdir%\outfile.txt"
(
FOR /f "usebackqtokens=1*delims=" %%e IN ("%filename1%") DO (
SET "line=%%e"
FOR /f "tokens=3 delims=|" %%y IN ("!line:||=|(missing)|!") DO ECHO %%y
)
)>"%outfile%"
TYPE "%outfile%"
GOTO :EOF
Always verify against a test directory before applying to real data.
Note that if the filename does not contain separators like spaces, then both usebackq and the quotes around %filename1% can be omitted.
The magic is that for each line, || is replaced by |(missing)|.
This simple solution has its faults - for instance if there is ||| in the source data, or the usual suspects (some punctuation symbols like !) but should be quite happy with alphameric source text.
Another way would be to use a third-party utility like sed to pre-process the source data.
The fundamental reason for this phenomenon is that for/f parses the line as [delimiters]token1[delimiters]token2..., where [delimiters] is any sequence of any of the delimiter characters.
I have a simple .bat with content:
#echo off
set "var=VAR1?VAR2?VAR3"
echo.%var%
echo.
for /F "delims=?" %%H in ('echo.%var%') do echo.%%~H
I would expect the following output:
VAR1?VAR2?VAR3
VAR1
VAR2
VAR3
Instead I get:
VAR1?VAR2?VAR3
VAR1
^ notice a trailing empty new line
Tried using different delimiter, usebackq, giving eol character, running through another for loop with different parameters, many other things; different variable name, different filename, setlocal w/o and w/ delayed expansion; no success.
If I include the tokens=1,2 parameter, it prints VAR1 into %%H and VAR2 into %%I as it should, but I need it to be "dynamic", give every element my %var% has.
EDIT: What is the most confusing to me is that if instead of writing my elements into a variable separated by a ?, I write it to a file, each element in its own line, then read the file line-by-line with "for /F "delims=*" %%H in ('type "VAR.txt"') do..."
it works perfeclty. Is it because the "delims=*", or is there a higher power messing with me? I don't see any difference from the FOR's perspective. If, by default, it should only give me the first token, why does it give every token from the file? Isn't token as new line the same as token as any other character?
You can also use this very simple trick that converts tokens into lines:
#echo off
setlocal EnableDelayedExpansion
set "var=VAR1?VAR2?VAR3"
echo %var%
echo/
for %%n in (^"^
%Do not remove this line%
^") do for /F "delims=" %%H in ("!var:?=%%~n!") do echo/%%~H
In this way, each token in the string: VAR1?VAR2?VAR3 is converted into separate lines like these ones:
VAR1
VAR2
VAR3
This is done replacing each ? character in the string by a NewLine (ASCII 10) character.
After that, you can process each line in any way you wish...
Each line read by a for/f is split into "tokens", substrings separated by any of the delimiters.
By default, tokens=1, so just the first token on the line is assigned to the metavariable %%H.
You could list each required token in a tokens= clause.
To assign each to %%H in turn, try
for %%H in (%var:?= %) do echo %%H
or if your elements contain spaces,
for %%H in ("%var:?=" "%") do echo.%%~H
The 'extra' newline is a red herring. You are misreading the result.
Here's a possible alternative methodology, which should be okay as long as each question mark delimited token does not contain a doublequote or exclamation mark character:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Rem Dehine a variable named var.
Set "var=VAR 1?VAR,2?VAR|3?VAR;4?VAR<5?VAR>6?VAR&7?VAR=8"
Rem view the content of the variable named var.
Set /P "=%var%" 0<NUL & Echo(
Rem Display an optional empty line
Echo(
Rem Undefine and variables beginning with the string var[.
For /F "Delims==" %%G In ('"(Set var[) 2>NUL"') Do Set "%%G="
Rem define a increment counter
Set "i=1"
Rem Enable delayed variable expansion
SetLocal EnableDelayedExpansion
Rem Define variables named var[increment] splitting var at each question mark.
Set "var[%i%]=%var:?=" & Set /A i +=1 & Set "var[!i!]=%"
Rem Disable delayed variable expansion
Rem and display the individual incremented variable values.
For /F "Tokens=1-2,* Delims=[]=" %%G In ('"(Set var[) 2>NUL"') Do (EndLocal
Set /P "=%%I" 0<NUL & Echo()
Rem Display an optional empty line
Echo(
Pause
Please be aware that if there are more than nine tokens, the display order will not currently match the order in the initial variable. adjustments would have to be made in order to do that.
Expected output:
VAR 1?VAR,2?VAR|3?VAR;4?VAR<5?VAR>6?VAR&7?VAR=8
VAR 1
VAR,2
VAR|3
VAR;4
VAR<5
VAR>6
VAR&7
VAR=8
Press any key to continue . . .
Here's another possibility:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Rem Define a variable named var.
Set "var=VAR 1?VAR,2?VAR;3?VAR=4?VAR!5?VAR&6?VAR)7?VAR^8?VAR#9"
Rem Display the content of the variable.
Set /P "=%var%" 0<NUL & Echo(
Rem Display an optional empty line.
Echo(
Rem Display the individual tokens.
For %%G In ("%var:?=","%") Do Set /P "=%%~G" 0<NUL & Echo(
Rem Display an optional empty line.
Echo(
Pause
This allows for an exclamation mark to exist in your file path tokens. It also has the additional benefit of being able to display the tokens in the same order, regardless of the number of them.
I should add that if you expect there to be percent characters in your tokens, you will have to perform an additional step, (due to those having a special meaning in cmd and batch files).
Expected output:
VAR 1?VAR,2?VAR;3?VAR=4?VAR!5?VAR&6?VAR)7?VAR^8?VAR#9
VAR 1
VAR,2
VAR;3
VAR=4
VAR!5
VAR&6
VAR)7
VAR^8
VAR#9
Press any key to continue . . .
I am currently working on a batch script that would move files from a source to a destination based on a master file. I wanted to add a condition that if there is a missing value from the columns then it would skip that line. I tried all the solutions that I found but it won't trigger the if condition. Here's a snippet of the code:
for /f "usebackq tokens=1-3 skip=1 delims=," %%a in (%MasterFilePath%) do (
set dest=%%c
set src=%%b
if !dest!=="" do (echo destination for source !src! is empty)
Here's a sample of the master file:
FolderName,SrcPath,DestPath
FolderA,C:\Work\FolderA,
FolderB,C:\Work\FolderB,C:\Work\Sample\Dest2
FolderC,C:\Work\FolderC,C:\Work\Sample\Dest3
The FOR /F command will always treat consecutive delimiters as one delimiter. So if you delimited file has an empty field and it does not have quotes, the fields will shift to the left on you. So to work around this you can two FOR /F commands. The first one will read in the whole line. That variable is then forced to put quotes around every field using string substitution. You can then use the second FOR /f command to split the fields up into the amount of fields you need.
#ECHO OFF
set "MasterFilePath=file.txt"
FOR /F "usebackq skip=1 delims=" %%G IN ("file.txt") DO (
set "line=%%G"
setlocal enabledelayedexpansion
SET "line="!line:,=","!""
for /f "tokens=1-3 delims=," %%A IN ('echo !line!') do (
IF NOT "%%~B"=="" IF NOT "%%~C"=="" ECHO ROBOCOPY "%%~B" "%%~C"
)
endlocal
)
For the sake of brevity I am just checking if both the source and destination is not empty and then echoing the robcopy command to the screen.
With your input example you would see this.
ROBOCOPY "C:\Work\FolderB" "C:\Work\Sample\Dest2"
ROBOCOPY "C:\Work\FolderC" "C:\Work\Sample\Dest3"
I now have the following bat file working (which allows one to add text to the end of each line of a file) -- please see also:
bat file: Use of if, for and a variable all together
#echo off
setLocal EnableDelayedExpansion
IF EXIST "%FileToModify1%" (
for /f "tokens=* delims= " %%a in (%FileToModify1%) do (
echo %%a Note: certain conditions apply >> "%SaveFile1%"
)
)
However, I would like to save each line to a variable (including the new line symbol(s)) and then echo the variable to a file at the end. Since there are several lines in the file it is really inefficient to save to a file with each line.
I tried googling this, but the answers do not fit my situation...
essentially I need the syntax for concatenating and saving to a variable (cumulatively like "+=" in C#), and also using the new lines...
Actually you do not need to put everything into a variable, you just need to place the redirection at another position.
Try this:
#echo off
setlocal EnableDelayedExpansion
if exist "%FileToModify1%" (
for /F "usebackq delims=" %%a in ("%FileToModify1%") do (
echo %%a Note: certain conditions apply
)
) > "%SaveFile1%"
endlocal
Note that empty lines in the original file are ignored by for /F, so they are not transferred to the new file. Also lines starting with ; are ignored by for /F (unless you change the eol option -- see for /?).
I modified the for /F options:
no delims are allowed, so the each line is output as is (with "tokens=* delims= ", leading spaces are removed from each line if present);
usebackq allows to surround the file specification in "" which is helpful if it contains spaces;
Appendix A
If you still want to store the file content into a variable, you can do this:
#echo off
setlocal EnableDelayedExpansion
rem the two empty lines after the following command are mandatory:
set LF=^
if exist "%FileToModify1%" (
set "FileContent="
for /F "usebackq delims=" %%a in ("%FileToModify1%") do (
set "FileContent=!FileContent!%%a Note: certain conditions apply!LF!"
)
(echo !FileContent!) > "%SaveFile1%"
)
endlocal
The file content is stored in variable FileContent, including the appendix Note: certain conditions apply. LF holds the new-line symbol.
Note:
The length of a variable is very limited (as far as I know, 8191 bytes since Windows XP and 2047 bytes earlier)!
[References:
Store file output into variable (last code fragment);
Explain how dos-batch newline variable hack works]
Appendix B
Alternatively, you could store the file content in a array, like this:
#echo off
setlocal EnableDelayedExpansion
if exist "%FileToModify1%" (
set /A cnt=0
for /F "usebackq delims=" %%a in ("%FileToModify1%") do (
set /A cnt+=1
set "Line[!cnt!]=%%a Note: certain conditions apply"
)
(for /L %%i in (1,1,!cnt!) do (
echo !Line[%%i]!
)) > "%SaveFile1%"
)
endlocal
Each line of the file is stored in an array Line[1], Line[2], Line[3], etc., including the appendix Note: certain conditions apply. cnt contains the total number of lines, which is the array size.
Note:
Actually this is not a true array data type as such does not exist in batch, it is a collection of scalar variables with an array-style naming (Line[1], Line[2],...); therefore one might call it pseudo-array.
[References:
Store file output into variable (first code fragment);
How to create an array from txt file within a batch file?]
you can write the output file in one shot:
(
for /l %%i in (0,1,10) do (
echo line %%i
)
)>outfile.txt
(much quicker than appending each line separately)
I have a .CSV that I am trying to sort through to create another file from the data, but when I run it through, it skips blank entries. For example, if a line is
value,value,value,,,value
and I try to get the 4th column, it would spit out 6th. Presumably because it is the next valid value. I don't want it to skip the blank entry as it can mess up the tables I'm trying to make. Anyone know how to resolve this? (Any tips are welcome as I suck at batch scripts)
Here is my script:
FOR /F "tokens=1,2,3,4,5,6,7,8,9,10,11,12,13,14 delims=," %%a in (file.csv) DO (
echo %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m %%n
)
pause
This is the standard behaviour of the FOR/F loop, consecutive delims only used as one delimiter.
But you can use a workaround with a second FOR/F.
Prefix each column with another character, split the line at the delim and remove the prefix.
setlocal EnableDelayedExpansion
FOR /F "delims=" %%L in (test.bat) DO (
set "line=%%L,,,,,,,,"
set "line=#!line:,=,#!"
FOR /F "tokens=1,2,3,4 delims=," %%a in ("!line!") DO (
set "param1=%%a"
set "param2=%%b"
set "param3=%%c"
set "param4=%%d"
set "param1=!param1:~1!"
set "param2=!param2:~1!"
set "param3=!param3:~1!"
set "param4=!param4:~1!"
echo !param1! !param2! !param3! !param4!
)
)
As jeb already mentiones in his answer, for /F treats consecutive delimiters as one. To avoid that, you could replace each delimiter , by "," and enclose each full line by "", so each field appears as being enclosed within "", which can easily be removed finally by the ~ modifier of the for /F variable; so there is no need to do any more string manipulations (like sub-string expansion) later on:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "usebackq delims=" %%# in ("file.csv") do (
set "LINE=%%#"
setlocal EnableDelayedExpansion
for /F "tokens=1-4 delims=," %%A in (^""!LINE:,="^,"!"^") do (
endlocal
echo Field 1: %%~A
echo Field 2: %%~B
echo Field 3: %%~C
echo Field 4: %%~D
setlocal EnableDelayedExpansion
)
endlocal
)
endlocal
This might not work properly if the data contain , characters by themselves (remember that , is not considered as delimiter in case a field is enclosed within "" in the original CSV file).
The toggling of delayed expansion is done to not lose any exclamation marks in the data.