For example I have a text file test.txt that contains like this:
Item1 $23 Item2 $24
Item3 $25
When I run this script:
#echo off
for /f "delims=$ tokens=1*" %%A in (test.txt) do echo %%B >> result.txt
The result is:
23 Item2 $24
25
I want the result is:
23
24
25
How should I repair the script?
Try the following script (see the explanatory rem remarks in the code):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_IFILE=test.txt"
set "_OFILE=result.txt"
rem // Store line-break in variable:
(set ^"LF=^
%= blank line =%
^")
rem // Write to resulting file:
> "%_OFILE%" (
rem // Loop through lines of text file:
for /F "usebackq delims=" %%L in ("%_IFILE%") do (
rem // Store current line in variable:
set "LINE=%%L"
rem // Toggle delayed expansion to be able to write and read variable in same code block:
setlocal EnableDelayedExpansion
rem // Replace each `$` by line-break and loop over resulting lines skipping the first one:
for /F "skip=1" %%K in (^"!LINE:$^=^%LF%%LF%!^") do (
rem // Return first token of each line:
echo(%%K
)
endlocal
)
)
endlocal
exit /B
At first, this replaces every $ by a line-break, so the first line of test.txt becomes:
Item1
23 Item2
24
And the second one becomes:
Item3
25
Then it extracts the first SPACE- or TAB-separated token from each of the newly formed multi-line strings, skipping the first line, so only the numbers are returned.
I have decided to post this example batch-file to show a method of parsing each line and returning every other string, (doesn't work with poison characters).
#(For /F "UseBackQDelims=" %%A In ("test.txt")Do #(Set "i=0"
For %%B In (%%A)Do #(Set /A "i=(i+1)%%2"&Cmd /V:On /Q /C Exit !i!
If Not ErrorLevel 1 Echo %%B)))>"results.txt"
In your posted example code and 'wanted' result, you could probably use that idea to output the strings with their first character removed if it is a $.
#(For /F "UseBackQDelims=" %%A In ("test.txt")Do #(Set "i=0"
For %%B In (%%A)Do #(Set /A "i=(i+1)%%2"&SetLocal EnableDelayedExpansion
If !i! Equ 0 (Set "Val=%%B"&If "!Val:~,1!"=="$" (Echo !Val:~1!
)Else Echo %%B)&EndLocal)))>"results.txt"
This should do exactly what you want.
#echo off
setlocal enabledelayedexpansion
(for /f "usebackq tokens=* delims= " %%i in ("test.txt") do (
set "_br=%%i"
echo(!_br: =^
!
)
)>_tmp
(for /f "delims=$ tokens=*" %%a in ('type _tmp ^| find "$"') do echo %%a)>result.txt
Note that you must copy the script as is. The extract of the script as show below does the replacement of whitespace with new line and therefore cannot change at all, the position and the extra line must remain as is for this to work as intended.
echo(!_br: =^
!
This script just splits each word separated by a space and redirects it to a new file, we then specifically search for the words containing $ and redirect those hits only.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /f "delims=" %%A in (test.txt) do (
for %%T in (%%A) do (
set "term=%%T"
if "!term:~0,1!"=="$" echo !term:~1!
)
)>> result.txt
endlocal
Read each line to %%A. Tokenise to %%T. Assign each token in turn to term, examine the first character and if $, echo the term, except the first character.
Related
I am trying to make a batch file that will concatenate a list but I want it to make a new line after 13 items. Below is the code that I am using to pull the data into one line but I am having issues limiting it. I have tried putting a counter on it but I am not implementing it correctly. Any help would be appreciated.
#echo off
setlocal enableextensions enabledelayedexpansion
set "var="
for /f "usebackq delims=" %%a in (%~1) do set "var=!var!%%a "
echo(%var%
)
Your supplied code did not really attempt the task, and you did not make clear what you meant by concatenating. What the following example does therefore, is write a new file, defined on line 4 with the content from your input file argument, %1, but with the content of each block of thirteen lines of input per line of output. I have separated each original line with a single whitespace character, based upon %%a in your own code.
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "InputFile=%~1"
Set "OutputFile=concatenated.txt"
If Not Exist "%InputFile%" Exit /B
For /F %%G In ('Copy /Z "%~f0" NUL') Do Set "CR=%%G" & (Set LF=^
% 0x0A %
)
SetLocal EnableDelayedExpansion
Set "i=0"
1>"%OutputFile%" (For /F UseBackQ^ Delims^=^ EOL^= %%G In ("%InputFile%"
) Do (Set /A "i += 1, # = i %% 13"
If !#! Equ 0 (Set /P "=%%G!CR!!LF!" 0<NUL) Else Set /P "=%%G " 0<NUL))
EndLocal
This is code from a call above it. I extract line 5 from a file with this code which leaves it in %%c. I then want to take each word in that line (there can be from 1 to nth words) and create a markdown link like
[word](word.html)
and append those to a txt file. This is what I have so far. If I have 10 words do I really have to add all the tokens in the command like %%d, %%e, etc.? Can I use something like tokens=1-*. * meaning the last token in the line? Using tokens=* uses the whole line of words as one token I believe.
setlocal enabledelayedexpansion
set "lineNr=5"
set /a lineNr-=1
for /f "usebackq delims=" %%c in (`more +!lineNr! "%~1"`) DO (
for /f "tokens=1-2 delims= " %%d in ("%%c") do (
echo [%%d]^(tags/%%d.html^) [%%e]^(tags/%%e.html^) ^<br^> >> index.txt
)
goto :eof
)
Here is an example of how you can do that. Note I just used some parts as an example as I do not have time to format escape characters now, but you'll get the idea :)
#echo off
set "lineNr=5"
set /a lineNr-=1
for /f "usebackq delims=" %%c in (`more +%lineNr% "%~1"`) DO set "line=%%c"
for %%i in (%line%) do echo| set /p =[%%i](tags/%%i.html)>>index.txt
OK, I got it working by doing this -
set "lineNr=5"
set /a lineNr-=1
for /f "usebackq delims=" %%c in (`more +%lineNr% "overview.md"`) DO (
set "line=%%c"
goto :next
)
:next
for %%i in (%line%) do echo| set /p =[%%i](tags/%%i.html) >> index.txt
Have to break out of the loop line listing after the first line wanted(line 5).
This is my first posting so if the format is not as it supposed to be please excuse me for this. (Suggestions for
improvement are welcome.)
I am trying to create a batchfile that will read last lines from logfiles and copy them to a new file.
Until now I have found here a way to read the last line.
Code would be something like:
for /f %%i in ('find /v /c "" ^< someFile.txt') do set /a lines=%%i
set /a startLine=%lines% - 1
more /e +%startLine% someFile.txt > lastLines.txt
The above code works for one file at a time. What I need is to read the last line from all files in a known list and add this line to a new .csv file.
I have been using the following code for getting the 4th entry in the logfiles but it returns every line of every logfile:
for /f %%x in (%list%) do for /f "delims=.txt, tokens=4" %%i in (%%x.txt) do echo %%x, %%i >> output.csv
What I would need is a sort of combination of both but I don't know how to combine them and make the complete last line be copied to the .csv file.
===
#Magoo:
Thanx for your reaction.
In every logfile can be 1 to >100 lines with comma separated information. Something like:
"LOGON,6-1-2015,12:43:39,USERNAME,HOSTNAME,,,,192.168.209.242,00:21:5A:2E:64:5E"
The last code with the 4th entry was used to get a list of all accounts that had logged in to the computers. This code gave me a very large list of all logon/logoff events on all computerlogs I checked in %list%.
In %list$ I had all the names of logfiles I wanted to be checked. This returned all lines.
For a new batchfile I need only the last logon/logoff entry and I want the whole last line.
So I have a .txt file with the hostnames of all computers I need to examine.
This .txt file will be read line by line via the variable %list%.
From every logfile I need only the last line copied to an output file.
===
I just tried the solution offered by JosefZ. Unfortunately this does not work for me yet. No lastlines are copied to the resultfile. In the code I removed the extra entry for possible lastlines for there are no empty lines in the logs, I also added an entry for the hostname I want to be available in the result. JosefZ had the filename there:
#ECHO OFF >NUL
#SETLOCAL enableextensions disabledelayedexpansion
type nul>output.csv
set "list=_listing.txt"
for /F "tokens=*" %%x in ('type "%list%"') do (
set "host=%%~x"
for /F "tokens=*" %%G in ('type "%%~x"') do set "lastline=%%G"
call :lline
)
:endlocal
#ENDLOCAL
goto :eof
:lline
set "filename=.\logs\%filename:&=^&%.txt"
echo %host%,%lastline%>>output.csv
goto :eof
The resultfile shows only the hostnames. I'll puzzle some more with this but all tips are welcome!
===
Got it!!!
#ECHO OFF >NUL
#SETLOCAL enableextensions disabledelayedexpansion
type nul>output.csv
set "list=_listing.txt"
for /F "tokens=*" %%x in ('type "%list%"') do (
set filename= :: *empty previous filename*
set lastline= :: *empty previous lastline*
set "host=%%~x"
set "filename=.\logs\%host%.txt" :: *creating the filename from path+hostname+extention*
for /F "tokens=*" %%G in ('type "%filename%"') do set "lastline=%%G"
call :lline
)
:endlocal
#ENDLOCAL
goto :eof
:lline
echo %host%,%lastline%>>output.csv
goto :eof
Your approach with line numbering could fail if a file has more trailing empty lines. Fortunately for /F loop ignores (does not iterate) empty lines; let's put to use this feature: in the script used next practices:
disabledelayedexpansion to allow ! in file names
set "list=_listing.txt" where the _listing.txt contains list of file names (full path and extension .txt including), one file name on one line: got by dir /b /s *.txt>_listing.txt
type nul>files\output.csv to empty the output file (optional)
set "lastline=!!!file empty!!!" to initialize variable %lastline%; could be set "lastline=" as well
call :lline to process variables %filename% and %lastline%
set "filename=%filename:&=^&%" to allow & in file names
The script is as follows:
#ECHO OFF >NUL
#SETLOCAL enableextensions disabledelayedexpansion
type nul>files\output.csv
set "list=_listing.txt"
for /F "tokens=*" %%x in ('type "%list%"') do (
set "filename=%%~x"
set "lastline=!!!file empty!!!"
rem the whole line
for /F "tokens=*" %%G in ('type "%%~x"') do set "lastline=%%G"
rem the fourth token only
rem for /F "tokens=4" %%G in ('type "%%~x"') do set "lastline=%%G"
call :lline
)
:endlocal
#ENDLOCAL
goto :eof
:lline
set "filename=%filename:&=^&%"
echo %filename% %lastline%
rem >>files\output.csv
goto :eof
Sample _listing.txt file:
d:\bat\files\1exclam!ation.txt
d:\bat\files\2exc!lam!ation.txt
d:\bat\files\11per%cent.txt
d:\bat\files\12per%cent%.txt
d:\bat\files\17per%Gcent.txt
d:\bat\files\18per%%Gcent.txt
d:\bat\files\21ampers&nd.txt
d:\bat\files\22ampers&&nd.txt
Output:
d:\bat>lastlines
d:\bat\files\1exclam!ation.txt 0 15.01.2015 1:52:28.48 -15072 20465
d:\bat\files\2exc!lam!ation.txt 6 15.01.2015 1:52:28.50 3250 16741
d:\bat\files\11per%cent.txt -8 15.01.2015 1:52:28.50 -3692 27910
d:\bat\files\12per%cent%.txt !!!file empty!!!
d:\bat\files\17per%Gcent.txt 0 15.01.2015 1:52:28.56 14508 12374
d:\bat\files\18per%%Gcent.txt 1 15.01.2015 1:52:28.56 30540 26959
d:\bat\files\21ampers&nd.txt 15.01.2015 1:22:50.18
d:\bat\files\22ampers&&nd.txt 15.01.2015 1:22:50.18
Honestly, all that ballast is for (possibly) trailing empty lines in files and for (possibly) ! and & in file names only; all could be done with
for /f %%x in (%list%) do for /f "skip=%startLine% tokens=4" %%i in (%%x) do echo %%x, %%i >> output.csv
You should use a simple FOR to iterate a list of values, not FOR /F.
Something like the following should work:
#echo off
setlocal enableDelayedExpansion
>>output.csv (
for %%F in (
"file1.log"
"file2.log"
"file3.log"
etc.
) do (
for /f %%A in ('find /v /c "" <%%F') do set /a skip=%%A-1
more +!skip! %%F
)
)
The quotes around the file names are there in case you get a name with spaces.
You could use your LIST variable if it looks something like
set LIST="file1.log" "file2.log" "file3.log" etc.
#echo off
setlocal enableDelayedExpansion
set LIST="file1.log" "file2.log" "file3.log" etc.
>>output.csv (
for %%F in (%LIST%) do (
for /f %%A in ('find /v /c "" <%%F') do set /a skip=%%A-1
more +!skip! %%F
)
)
If any of your file names contain the ! character, then you must toggle delayed expansion ON and OFF within your loop. Otherwise the delayed expansion will corrupt the names when %%F is expanded.
#echo off
setlocal disableDelayedExpansion
set LIST="file1.log" "file2.log" "file3.log" etc.
>>output.csv (
for %%F in (%LIST%) do (
for /f %%A in ('find /v /c "" <%%F') do set /a skip=%%A-1
setlocal enableDelayedExpansion
more +!skip! %%F
endlocal
)
)
I have a file which I need to load into a database. It has a delimiter of pipe (|) however each line contains different number of pipes. Using a batch script, how can I remove pipes from each line so the same number of pipes are on each line?
Example of file:
1|2|3||||||
4|5|6|||
7|8||||||
Let's say I'd like 5 pipes on each line only so it looks like:
1|2|3|||
4|5|6|||
7|8||||
Update See second solution and limitation updates.
Example file.txt contents
A|B|C|D|E|F|G
1|2|3|4|5|6|7
!|#|#|$|%|^|&
]1|]2|]3|]4|]5|]6|]7
|Two||Four||||Eight
!#$%^&%^*(){}|[]';/.,<>/|
Lonely||||||||||||||||||
Sep|er|ate| From| Th|e |W||orld | |
First Solution
Here is a simple way to do what you want. It should not have any problems with special characters.
Limitations
It only supports up to 24 25 columns as it is currently written. %%A to %%Y
The first value may not begin with ]. Replaced for /F "tokens=1,* delims=]" %%Y in ('type file.txt ^| find /v /n ""') do ( with for /F "delims=" %%Z in ('type file.txt') do (.
"Empty" fields may only appear at the end of every line. See second solution.
Does not preserve blank lines in the file. (This can be fixed if desired.)
Just specify how many and which columns you want to keep. For example tokens=3-5,12,48-50 will select only columns 3,4,5,12,48,49,50. Make sure you add on or remove the variables to match the output you want. echo %%A^|%%B^|%%D^|%%C^|%%G^|%%E^|%%F. Note that the columns can be reordered as well in the echo statement.
#echo off
setlocal DisableDelayedExpansion
for /F "delims=" %%Z in ('type file.txt') do (
for /F "tokens=1-5 delims=|" %%A in ("%%Z") do (
echo %%A^|%%B^|%%C^|%%D^|%%E
)
)
endlocal
pause >nul
You can either redirect the output of the .bat file into a new file Script.bat>output.txt or output the echo command to a file by appending >>output.txt to the echo line.
Example Output:
A|B|C|D|E
1|2|3|4|5
!|#|#|$|%
]1|]2|]3|]4|]5
Two|Four|Eight|| <-- Note that this line exhibits limit 3.
!#$%^&%^*(){}|[]';/.,<>/|||
Lonely||||
Sep|er|ate| From| Th
Second Solution
Shares only limitations 1 and 4. Currently adds spaces into existing blank columns to preserve all columns. They can be removed with a further code change, but will not add unless desired by the OP.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "delims=" %%Z in ('type file.txt') do (
set "xLine=|%%Z"
call :Parse xLine
)
endlocal
pause >nul
goto :eof
:Parse
call set "xLine=%%%~1:||=| |%%"
for /F "tokens=1-5 delims=|" %%A in ("%xLine%") do (
echo %%A^|%%B^|%%C^|%%D^|%%E
)
goto :eof
Example Output:
A|B|C|D|E
1|2|3|4|5
!|#|#|$|%
]1|]2|]3|]4|]5
|Two| |Four|
!#$%^&%^*(){}|[]';/.,<>/|||
Lonely| | | |
Sep|er|ate| From| Th
There is no direct way to achieve this process, so each character must be revised in order to count the number of pipes in each line. It works, but it is somewhat slow.
#echo off
setlocal EnableDelayedExpansion
rem Number of desired pipes
set limit=5
for /F "delims=" %%a in (input.txt) do (
set "line=%%a"
rem Get position of last character
set last=0
for /L %%b in (12,-1,0) do (
set /A "last|=1<<%%b"
for %%c in (!last!) do if "!line:~%%c,1!" equ "" set /A "last&=~1<<%%b"
)
rem Copy each character to result, but just %limit% number of pipes
set pipes=0
set result=
for /L %%c in (0,1,!last!) do (
if "!line:~%%c,1!" neq "|" (
set "result=!result!!line:~%%c,1!"
) else (
set /A pipes+=1
if !pipes! leq %limit% set "result=!result!|"
)
)
echo !result!
)
Previous program will fail if the input line contain exclamation marks.
Output:
1|2|3|||
4|5|6|||
7|8||||
Antonio
I have successfully made a script that filters out duplicate lines in a file and saves the results to a variable semi-colon separated (sort of an "array"). I could not find any real good solution to it.
#echo off
setlocal enabledelayedexpansion
rem test.txt contains:
rem 2007-01-01
rem 2007-01-01
rem 2007-01-01
rem 2008-12-12
rem 2007-01-01
rem 2009-06-06
rem ... and so on
set file=test.txt
for /f "Tokens=* Delims=" %%i in ('type %file%') do (
set read=%%i
set read-array=!read-array!;!read!
)
rem removes first trailing ";"
set read-array=!read-array:*;=!
echo !read-array!
for /f "Tokens=* Delims=" %%i in ('type %file%') do (
set dupe=0
rem searches array for the current read line (%%i) and if it does exist, it deletes ALL occurences of it
echo !read-array! | find /i "%%i" >nul && set dupe=1
if ["!dupe!"] EQU ["1"] (
set read-array=!read-array:%%i;=!
set read-array=!read-array:;%%i=!
)
rem searches array for the current read line (%%i) and if it does not exist, it adds it once
echo !read-array! | find /i "%%i" >nul || set read-array=!read-array!;%%i
)
rem results: no duplicates
echo !read-array!
Contents of !read-array! is 2008-12-12;2007-01-01;2009-06-06
I now want to take out each item in the array and write them to a new file, with line breaks after each item. Example:
2008-12-12
2007-01-01
2009-06-06
So this is what I've come up with so far.
The problem I'm having is that the second for-loop doesn't accept the !loop! variable as a token definition when being nested. It does however accept %loop% if it's not nested.
The reason I'm doing it this way is that the !read-array! may have a unknown number of items, therefore I count them as well.
Any ideas?
rem count items in array
set c=0
for %%i in (!read-array!) do set /a c+=1
echo %c% items in array
for /l %%j in (1,1,%c%) do (
set loop=%%j
for /f "Tokens=!loop! Delims=;" %%i in ("!read-array!") do (
echo %%i
rem echo %%i>>%file%
)
)
exit /b
At end of your first section, when contents of !read-array! is 2008-12-12;2007-01-01;2009-06-06, you may directly separate the elements of your "list" with a simple for because the standard separators in Batch files may be, besides spaces, comma, semicolon or equal signs:
for %%i in (%read-array%) do echo %%i
However, may I suggest you a simpler method?
Why not define a "real" array with the subscript value of the lines? This way, several repeated lines will store its value in the same array element. At end, just display the values of the resulting elements:
#echo off
set file=test.txt
for /F "Delims=" %%i in (%file%) do (
set read-array[%%i]=%%i
)
rem del %file%
for /F "Tokens=2 Delims==" %%i in ('set read-array[') do (
echo %%i
rem echo %%i>>%file%
)
EDIT
Alternative solution
There is another method that assemble a list of values separated by semicolon as you proposed. In this case each value is first removed from previous list content and immediately inserted again, so at end of the cycle each value is present just once.
#echo off
setlocal EnableDelayedExpansion
set file=test.txt
for /F "Delims=" %%i in (%file%) do (
set read-array=!read-array:;%%i=!;%%i
)
rem del %file%
for %%i in (%read-array%) do (
echo %%i
rem echo %%i>> %file%
)