Third, and hopefully final revamp of question...
Batch file reading text file with for loop line by line into variable. Each line of said text file can be formatted completely different from the next. The only common delimiter would be a four-digit number (year) somewhere in each line. The goal is to return whatever text follows the aforementioned four-digit number for each line via echo.
Text file example:
Monday, January 1, 1900 there was an event-e6718
On this day in 1904 nothing occurred
Wednesday, March 3, 1908 an error occurred when attempting to access the log
Thursday, , 1911 - access denied
Friday, in whatever month, on whatever day, in 1938, nothing happened
Therefore, based on the above text file example, the return would be like...
there was an event-e6718
nothing occurred
an error occurred when attempting to access the log
- access denied
nothing happened
As of 1318 PST, I've tried every code snippet in the comments following, but none of them were able to return the data I need to return.
However, those comments were related to my initial question which has since been markedly improved.
I've even tried a regular expression of "^[1-9][0-9][0-9][0-9]$", however I'm new to regex, so I'm sure I have that wrong.
Is this even possible?
Thanks in advance.
Batch is a horrible task for this. REGEX is a good tool, but cmd doesn't support it (with the exception of a very crippled subset with findstr). If you are willing to use an external tool, it gets easy:
<old.txt call jrepl ".*(\d{4})\D\ *(.*$)" "$2" >new.txt
search for a four digit number \d{4}, followed by a non-digit \D and zero or more spaces, and take the rest until "EndOfLine" .*$. (Parentheses) mark matches, referenced by $x. Your desired strings are in $2.
Output with your example file:
there was an event-e6718
nothing occurred
an error occurred when attempting to access the log
- access denied
there was an event-dsfd318
nothing happened
Should you decide to include the year, you can find it in $1:
<old.txt call jrepl ".*(\d{4})\D\ *(.*$)" "$1: $2" >new.txt
gives:
1900: there was an event-e6718
1904: nothing occurred
1908: an error occurred when attempting to access the log
1911: - access denied
1910: there was an event-dsfd318
1938: nothing happened
The call is neccessary wihtin a batch file, because jrepl is a batch file and therefore wouldn't return without call.
(The REGEX pattern might be subject for improvement; I don't have much experience with it yet.)
jrepl.bat was programmed by dbenham.
Give this a go:
#echo off & setlocal enabledelayedexpansion
for /f "delims=" %%i in ('type "C:\textfile.txt" ^| findstr /IRC:"there was an event"') do (
set "event=%%i"
echo "!event:*there was an event=there was an event!"
)
textfile.txt
Monday, January 1, 1900 there was an event-e6718
On this day in 1904 nothing occurred
Wednesday, March 3, 1908 an error occurred when attempting to access the log
Thursday, , 1911 - access denied
Monday, January 1, 1910 there was an event-dsfd318
Friday, in whatever month, on whatever day, in 1938, nothing happened
Result:
If there truly is no common point in the string or consistency in token count, adjust the number of iterations in the below for loop to match the maximum possible tokens.
#Echo off & Setlocal EnableDelayedexpansion
Set "event=Monday, January 1, 1900 there was an event-e6718"
For /L %%i in (1 1 10) Do (
Set "event=!event:*, =!"
)& rem // arbitrary number of iterations that should be adjusted to match the maximum expected tokens
Set "event=%event:~5,100%"& rem // remove the year[space] from the string - final string maximum length is also arbitrary and may need adjusting.
Echo/%event%
** Update **
An example of a macro to get the last token within a for loop using a macro version of the above approach:
Note: you'll need to adjust the file path for yout input file.
#Echo off
(Set \n=^^^
%=Newline Var=%
)
Set Gettoken=For %%n in (1 2) Do if %%n==2 (%\n%
For /F "Delims=" %%G in ("!string!") Do (%\n%
Set "output=%%G"%\n%
For %%D in ("!Delim!") Do For /L %%i in (1 1 10) Do Set "output=!output:*%%~D=!"%\n%
Set "output=!output:~5,100!"%\n%
)%\n%
Set output%\n%
) Else Set string=
Setlocal EnableDelayedExpansion
Set "Delim=, "&& For /F "Delims=" %%I in (inputfile.txt) Do %GetToken%%%I
Edit:
single batch solution for the amended questions actual requirement.
#Echo off & CD "%~dp0"
Setlocal Enabledelayedexpansion
rem // replace inputfile.txt with your filepath
For /F "Delims=" %%L in (inputfile.txt) Do (
Call :sub "%%L"
rem // the below for loop will remove everything up to including the first year from the string
rem // as well as traling coma[space] / [space]
For %%E in (!Errorlevel!) Do (
If Not "%%E"=="0" (
Set "String=!String:*%%E=####!"
Set "String=!String:####, =!"
Set "String=!String:#### =!"
Set "String=!String:####=!"
)
)
rem // output only if a year "delimiter" was encountered
If not "%%~L"=="!String!" Echo/!String!
)
Exit /b
:sub
Set "String=%~1"
rem // adjust for loop %%I for valid year range and %%# for maximum expected string length
For /L %%I in (1899 1 2050) Do (For /L %%# in (0 1 100) Do (If "!String:~%%#,4!"=="%%I" (Exit /B %%I)))
Exit /B 0
Related
I have a .txt file of which name is used as a reference with the format AS2204-1 according to the naming scheme ASyymm-sn with yy being the current year without century, mm being the current month and sn being a serial number with the current month. I try to get the current file name and increment the serial number by 1 on year and month unchanged, copy the new file name to the clipboard and then rename the text file.
This is my code so far:
#echo off
set yy=%date:~12,2%
set mm=%date:~4,2%
set /a sn=0
for %%a in ('dir *.txt') do (set filename=%%a)
set Fmm=%filename:~5,2%
if %Fmm%==%mm% (set /a sn=sn+1) else (set /a sn=1)
echo AS%yy%%mm%^-%sn% |clip
ren "%filename%.txt" "AS%yy%%mm%^-%sn%.txt"
I can't get the file name assigned to the variable filename.
What is wrong with my code and what would be a correct FOR loop?
#echo off
setlocal
pushd "?:\whichever\directory\contains your\target files"
set "yy=%date:~12,2%"
set "mm=%date:~4,2%"
set "filename="
for /f %%a in ('dir /b /a-d /od AS%yy%%mm%-*.txt 2^>nul') do (set "filename=%%~na")
rem use this line if you use suppressed leading zero
if defined filename (set /a "sn=%filename:*-=%+1") else (set /a sn=1)
rem use this line if you use 2-digit
if defined filename (set /a "sn=1%filename:~-2%+1") else (set /a sn=101)
IF %sn% gtr 100 SET "sn=%sn:~-2%"
echo AS%yy%%mm%-%sn%
ren "whatever.txt" "AS%yy%%mm%-%sn%.txt"
POPD
goto :eof
The setlocal ensures that the environment changes (new variables) are removed when the batch terminates.
pushd switches to the specified directory. I've no idea what your directory is.
The syntax SET "var=value" (where value may be empty; in which case var becomes undefined) is used to ensure that any stray trailing spaces are NOT included in the value assigned.
The for /f assigns each line of the dir "report" to %%a in turn. In consequence, filename will be set to the name part only (%%~na) of the filename found. dir /b produces a list of files to be processed, in basic form (filename only). /a-d excludes directorynames, /od produces the list in date order. The filemask AS%yy%%mm%-*.txt asks for all .txt files which match AS+thecurrentyearnumber+thecurrentmonthnumber+-, so it's not necessary to check the year/month part. The 2^>nul suppresses error messages should no filename matching the mask be found.
If a filename was found, filename will be defined. That's why it's set to nothing (which undefines it) before the for loop.
The filenames must be produced in date order because ASyymm-10 will sort before ASyymm-2 in the default name-order list.
Well, I've no idea whether you use 1- or 2-digit serial numbers - the processing is different.
If you use the first if defined... then sn is set to 1 more than (that part of filename that exists when all characters up to the - are removed) or 1 if no prior file was found.
If you use the second if defined... then sn is set to 1 more than (1+the last 2 characters of filename) or 101 if no prior file was found, so ASyymm-07 would produce sn=108. This is necessary because batch interprets a numeric string beginning 0 as OCTAL in calculations.
if the result is >100, then use the last 2 characters of sn.
I've just echoed the required string to the screen. Echo to the clipboard if you wish.
Well - the rename. It's unclear what the file to be renamed to the calculated name is, so I've used whatever. Possibly it's a file named ASyymm.txt - if so, replace whatever with AS%yy%%mm%.
The popd returns to the original directory.
Documentation can be obtained by executing commandname /? for most commands but it can be a little cryptic at times. Simply search SO for examples.
There could be used the following batch file for this task:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /F "tokens=1,2 delims=/" %%G in ('%SystemRoot%\System32\robocopy.exe "%SystemDrive%\|" . /NJH') do set "YearMonth=%%G%%H" & goto SearchFile
:SearchFile
set "SerialNumber=0"
set "FileNameScheme=AS%YearMonth:~2%-"
for %%G in ("%FileNameScheme%*.txt") do for /F "tokens=2 delims=-" %%H in ("%%~nG") do if %%H GTR !SerialNumber! (set "SerialNumber=%%H" & set "FileName=%%G")
set /A SerialNumber+=1
echo %FileNameScheme%%SerialNumber%| %SystemRoot%\System32\clip.exe
if defined FileName (
ren "%FileName%" "%FileNameScheme%%SerialNumber%.txt"
) else (
rem del /Q "AS????-*.txt" >nul 2>nul
echo %FileNameScheme%%SerialNumber%>"%FileNameScheme%%SerialNumber%.txt"
)
endlocal
The first two lines define the required execution environment completely which is:
command echo mode turned off
command extensions enabled
delayed variable expansion enabled
Please read the chapter Usage of ROBOCOPY to get current date/time in my answer on Time is set incorrectly after midnight for an explanation of the FOR command line which uses ROBOCOPY to get current year and month independent on which country (region) is configured for the used account which determines the date format of the dynamic variable DATE.
The environment variable SerialNumber is defined first with default value 0.
The environment variable FileNameScheme is defined with AS at beginning, the current year without the century with always two digits, the current month with always two digits and - which results today in the string AS2204-.
The command FOR is used to search in current directory (can by any directory) for one or more non-hidden files matching the wildcard pattern AS2204-*.txt.
It is unclear how my files can be in the current directory matching the wildcard pattern AS????-*.txt. Therefore the code is written to work also with multiple files matching this wildcard pattern in the current directory.
For each file name matching the wildcard pattern with current year and month already included one more FOR loop is used to get the string between the hyphen and the file extension which is hopefully always a number in range 0 to 2147483647 without leading zeros.
This number string is compared with the current value of the environment variable SerialNumber. Delayed variable expansion must be enabled because of this number comparison. If the number of the current file is greater the current serial number, the greater number is used further as serial number and the name of the current file is assigned to the environment variable FileName.
Please note that GTR does not work on number in file has a leading zero as in this case the number would be interpreted as an octal instead of a decimal number which means 08 and 09 would be interpreted as invalid octal numbers with using in this case value 0 for the number comparison.
The default value 0 or the greatest number of files matching AS2204-*.txt is next incremented by one using an arithmetic expression.
The file name with the incremented serial number is copied without file extension to the clipboard for whatever purpose.
If there was really found a file with current year and month in its name, this file is now renamed to contain the serial number incremented by one. Otherwise a new file is created with the new file name and containing the file name without file extension as data, for example AS2204-1.
There is a line commented out with command REM which would delete all files matching the wildcard pattern AS????-*.txt, except hidden files ignored by command DEL by default and read-only files which are not deleted by command DEL by default and matching files being currently opened by an application. It is not clear what to do with the other file(s) matching this pattern from former month(s).
The batch file code could be optimized to following command lines if there is either no or always just one file matching the wildcard pattern AS????-*.txt in the current directory:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "tokens=1,2 delims=/" %%G in ('%SystemRoot%\System32\robocopy.exe "%SystemDrive%\|" . /NJH') do set "YearMonth=%%G%%H" & goto SearchFile
:SearchFile
set "SerialNumber=0"
set "FileNameScheme=AS%YearMonth:~2%-"
for %%G in ("%FileNameScheme%*.txt") do for /F "tokens=2 delims=-" %%H in ("%%~nG") do set "SerialNumber=%%H"
set /A SerialNumber+=1
echo %FileNameScheme%%SerialNumber%| %SystemRoot%\System32\clip.exe
if exist "AS????-*.txt" (
ren "AS????-*.txt" "%FileNameScheme%%SerialNumber%.txt"
) else (
echo %FileNameScheme%%SerialNumber%>"%FileNameScheme%%SerialNumber%.txt"
)
endlocal
Delayed variable expansion is no longer needed which makes the processing of the batch file a very little bit faster. Serial numbers with one or more leading zeros would be still a problem because of command SET on evaluation of an arithmetic expression converts the number string also to an integer with interpreting the number as octal number on having the character 0 at beginning of the number string.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
del /?
echo /?
endlocal /?
for /?
goto /?
if /?
rem /?
robocopy /?
set /?
setlocal /?
See also:
Microsoft documentation about Using command redirection operators
Single line with multiple commands using Windows batch file for an explanation of the unconditional command operator &.
first of all thanks for all the answers!
I did it that way - it works fine until the renaming part that just doesn't work - any idea why?
set "yy=%date:~12,2%"
set "mm=%date:~4,2%"
set "filename=\root\*.txt"
for %%A in (%filename%) do (set x=%%~nA)
set Fmm=%x:~4,2%
set sn=%x:7.1%
if %Fmm%==%mm% (set /a sn=sn+1) else (set /a sn=1)
echo AS%yy%%mm%^-%sn% |clip
set newfilename=AS%yy%%mm%-%sn%.txt
set oldfilename=%x%.txt
ren %oldfilename% %newfilename%
This is working, but doesn't feel elegant to me. I'm creating an automated movie archive script in batch and would like to automatically find a movie title based on the disc volume name. The web query is done via tmdb, but returned results is difficult to parse since it isn't meant for batch. The results would be a contiguous line like:
{"page":1,"results":[{"poster_path":"\/5ttOaThDVmTpV8iragbrhdfxEep.jpg","adult":false,"overview":"At the height of the Cold War, a mysterious criminal organization plans to use nuclear weapons and technology to upset the fragile balance of power between the United States and Soviet Union. CIA agent Napoleon Solo and KGB agent Illya Kuryakin are forced to put aside their hostilities and work together to stop the evildoers in their tracks. The duo's only lead is the daughter of a missing German scientist, whom they must find soon to prevent a global catastrophe.","release_date":"2015-08-13","genre_ids":[35,28,12],"id":203801,"original_title":"The Man from U.N.C.L.E.","original_language":"en","title":"The Man from U.N.C.L.E.","backdrop_path":"\/bKxcCNv2xq8M3GD5iSrv9bMGDVa.jpg","popularity":5.346674,"vote_count":1842,"video":false,"vote_average":7},{"poster_path":"\/3VScfiBmE1loQxMkuN1suALv4f8.jpg","adult":false,"overview":"When THRUSH steals a nuclear weapon and demands a ransom delivered by Napoleon Solo, UNCLE recalls him and his partner to duty.","release_date":"1983-04-05","genre_ids":[28,80,53,10770],"id":94116,"original_title":"The Return of the Man from U.N.C.L.E.: The Fifteen Years Later Affair","original_language":"en","title":"The Return of the Man from U.N.C.L.E.: The Fifteen Years Later Affair","backdrop_path":"\/5LGBhGg5Tj9OSW4rD0itz0sYKPT.jpg","popularity":1.046707,"vote_count":5,"video":false,"vote_average":3.6}],"total_results":2,"total_pages":1}
You don't really know what you're going to get or how many titles will be returned. Dumping this into a file and reading back tokens doesn't make sense. The delimiter is a string (,") so I've come up with the following script which does function.
#echo off
setlocal EnableDelayedExpansion
set _tmdbReturn=
set _metaDataFile=
set _metaDataFile="C:\some path\metaData.txt"
set _metaDataFile=%_metaDataFile:~1,-1%
:: Do a movie title search based on a Disc Volume Label
for /f "usebackq delims=" %%a in (`PowerShell -Command "(new-object net.webclient).DownloadString('https://api.themoviedb.org/3/search/movie?api_key=xxx&query=The+Man+from+uncle')"`) do (set _tmdbReturn=%%a)
:: Result is in a contiguous string and the delimiter is a string with a comma and double quotes (,")
:: Replace delimiter string with a single character that does not occur in tmdb data
set _tmdbReturn=%_tmdbReturn:,"=#"%
set _tmdbReturn=%_tmdbReturn:"=%
:: replace unique single character with a line feed
set _tmdbReturn=!_tmdbReturn:#=^
!
:: Eliminate the special character
set _tmpdbReturn=!_tmdbReturn:#=!
:: Rewrite data to txt file with row separated data.
echo !_tmdbReturn!>"%_metaDataFile%"
set x=
set /a x=0
for /f "tokens=* delims=#" %%a in ('type "%_metaDataFile%"') do (
if !x!==0 (
set _newline=%%a
echo !_newline!>"%_metaDataFile%"
) else (
set _newline=%%a
echo !_newline!>>"%_metaDataFile%"
)
set /a x+=1
)
My question is two fold...is there a better way to do this? I also have not figured out how to write to the _metaDataFile without first dumping !_tmdbReturn! into a txt file. I've tried replacing the command of the last For Loop with
for /f "tokens=* delims=#" %%a in ('echo !_tmdbReturn!') do (
Only the first token writes yet
echo !_tmdbReturn!
displays the data properly producing the following:
{page:1
results:[{poster_path:\/5ttOaThDVmTpV8iragbrhdfxEep.jpg
adult:false
overview:At the height of the Cold War, a mysterious criminal organization plans to use nuclear weapons and technology to upset the fragile balance of power between the United States and Soviet Union. CIA agent Napoleon Solo and KGB agent Illya Kuryakin are forced to put aside their hostilities and work together to stop the evildoers in their tracks. The duo's only lead is the daughter of a missing German scientist, whom they must find soon to prevent a global catastrophe.
release_date:2015-08-13
genre_ids:[35,28,12]
id:203801
original_title:The Man from U.N.C.L.E.
original_language:en
title:The Man from U.N.C.L.E.
backdrop_path:\/bKxcCNv2xq8M3GD5iSrv9bMGDVa.jpg
popularity:5.346674
vote_count:1842
video:false
vote_average:7},{poster_path:\/3VScfiBmE1loQxMkuN1suALv4f8.jpg
adult:false
overview:When THRUSH steals a nuclear weapon and demands a ransom delivered by Napoleon Solo, UNCLE recalls him and his partner to duty.
release_date:1983-04-05
genre_ids:[28,80,53,10770]
id:94116
original_title:The Return of the Man from U.N.C.L.E.: The Fifteen Years Later Affair
original_language:en
title:The Return of the Man from U.N.C.L.E.: The Fifteen Years Later Affair
backdrop_path:\/5LGBhGg5Tj9OSW4rD0itz0sYKPT.jpg
popularity:1.046707
vote_count:5
video:false
vote_average:3.6}]
total_results:2
total_pages:1}
I'm attempting to redirect echo !_tmdbReturn! to the Find function extracting a particular value by name. I can do it in a file using findstr, but was trying it on the variable. I'm not fluent in batch so any suggestions are appreciated.
In case its useful for someone I settled on the following:
set x=
set /a x=0
set y=
set /a y=0
:: clean up the beginning of the data replace {" with " so poster_path is passed as a value
set _tmdbReturn=%_tmdbReturn:{"="%
set _tmdbReturn=%_tmdbReturn:~0,-1%
for /F "tokens=1* delims=[" %%a in ("!_tmdbReturn!") do ( set _tmdbReturn=%%b)
rem Separate the string in lines at ," delimiter
for /F "delims=" %%a in (^"!_tmdbReturn:^,^"^=^
% Do NOT remove this line %
!^") do (
set "line=%%a"
rem Eliminate quotes
set "line=!line:"=!"
rem Show lines of desired values only
for /F "tokens=1* delims=:" %%b in ("!line!") do (
if "%%b" equ "poster_path" set /a x+=1
if "%%b" equ "total_results" (
call set _movie.%%b=%%c
) else (
echo call set _movie[!x!].%%b=%%c
call set _movie[!x!].%%b=%%c
)
)
)
This give me an array of the returned results with structured object properties that I can use as my script morphs. This may be old hat to most, but I'm having fun!
The code below separates your long string in several lines:
#echo off
setlocal EnableDelayedExpansion
:: Do a movie title search based on a Disc Volume Label
:: for /f "usebackq delims=" %%a in (`PowerShell -Command "(new-object net.webclient).DownloadString('https://api.themoviedb.org/3/search/movie?api_key=xxx&query=The+Man+from+uncle')"`) do (set _tmdbReturn=%%a)
for /F "delims=" %%a in (input.txt) do set "_tmdbReturn=%%a"
rem Separate the string in lines at ," delimiter
for /F "delims=" %%a in (^"!_tmdbReturn:^,^"^=^
% Do NOT remove this line %
!^") do (
set "line=%%a"
rem Eliminate quotes
echo !line:"=!
)
I stored your long line in input.txt file for my testings.
About "redirect echo !tmdbReturn! to the Find function extracting a particular value by name"; if you show what exactly you want, perhaps I could show you how to get an equivalent result in a simpler way (without using find command)...
EDIT: Show just desired values
If you want not to create a file with all lines, but just show the lines of a desired value, then you may directly look for such a value in each line:
#echo off
setlocal EnableDelayedExpansion
for /F "delims=" %%a in (input.txt) do set "_tmdbReturn=%%a"
rem Separate the string in lines at ," delimiter
for /F "delims=" %%a in (^"!_tmdbReturn:^,^"^=^
% Do NOT remove this line %
!^") do (
set "line=%%a"
rem Eliminate quotes
set "line=!line:"=!"
rem Show lines of desired values only
for /F "tokens=1* delims=:" %%b in ("!line!") do (
if "%%b" equ "original_title" echo %%b: %%c
)
)
You may also look for several values; just define the list of the desired values at beginning, enclosing all values by a certain delimiter character:
set "values=/title/original_title/"
... and change the if command inside the for by this one:
if "!values:/%%b/=!" neq "%values%" echo %%b: %%c
I want to get the first string of the second line of a text file using a batch file, If possible get the first string of any line I want.
This is the text I want to parse:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
94c9dc4ba7c6 clearcmd6 "c:\\windows\\system..." 24 minutes ago Exited (0) 23 minutes ago mycont
So I expect to get 94c9dc4ba7c6 inside a parameter.
Is that possible?
Batch-file:
#echo off
for /f "USEBACKQ tokens=1 skip=1 delims= " %%a in (text-file.txt) do (echo %%a)
text-file.txt
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
94c9dc4ba7c6 clearcmd6 "c:\\windows\\system..." 24 minutes ago Exited (0) 23 minutes ago mycont
Output:
94c9dc4ba7c6
You would need to bear in mind that the first string would be up to the first whitespace.
#Echo Off
Set "file=text-file.txt"
Set "line=34"
Set/A "skip=line-1"
For /F "UseBackQ Skip=%skip%" %%A In ("%file%") Do Set "var=%%A"
Setlocal EnableDelayedExpansion
Echo(!var!
EndLocal
GoTo :EOF
You just input your required file name replacing text-file.txt on line number 2 and line number replacing 34 on line 3.
I am trying to print Line 4, Col 21-50 out of a text file, can this be simply done under Windows somehow? I've been trying to do this:
FOR /F "usebackq tokens=1 delims=-" %G IN (%COMPUTERNAME%.txt) DO ECHO %G
This is just working out terribly. Can't I just print a specific set of lines?
I need this script to be run on multiple computers, ideally I'd like to convert it to a variable for use with slmgr -ipk, maybe someone has a better suggestion?
Contents of text file (I want the XXXXX-XXXXX-XXXXX-XXXXX-XXXXX portion):
==================================================
Product Name : Windows 7 Professional
Product ID : 00371-OEM-9044632-95844
Product Key : XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
Installation Folder : C:\Windows
Service Pack : Service Pack 1
Computer Name : LIBRA
Modified Time : 6/4/2015 7:26:54 PM
==================================================
if you want only the "Product Key" line you can try with
type %COMPUTERNAME%.txt|find /i "Product Key"
or
for /f "tokens=2 delims=:" %%# in (' type %COMPUTERNAME%.txt^|find /i "Product Key"') do echo %%#
For the task at hand, npocmaka's answer is the best suitable approach, as it does not insist on a fixed position of the string to extract from the file.
However, I want to provide a variant that sticks to a certain position.
The following code extracts the string placed at columns 21 to 50 in line 4 of file list.txt (the result is echoed (enclosed in "") and stored in variable LINE_TXT (without ""):
#echo off
for /F "tokens=1,* delims=:" %%L in (
'findstr /N /R ".*" "list.txt"'
) do (
if %%L equ 4 (
set "LINE_TXT=%%M"
goto :NEXT
)
)
:NEXT
if defined LINE_TXT set "LINE_TXT=%LINE_TXT:~20,29%"
echo."%LINE_TXT%"
The goto :NEXT command terminates the for /F loop at the given line; this is not mandatory but will improve performance for huge files (as long as the given line number is quite small).
To be more flexible, the following code can be used (define the string position in the initial set block):
#echo off
rem Define the string position here:
set FILE_TXT="list.txt"
set LINE_NUM=4
set COL_FROM=21
set COL_UPTO=50
setlocal EnableDelayedExpansion
set /A COL_UPTO-=COL_FROM
set /A COL_FROM-=1
for /F "tokens=1,* delims=:" %%L in (
'findstr /N /R ".*" %FILE_TXT%'
) do (
if %%L equ %LINE_NUM% (
set "LINE_TXT=%%M"
if defined LINE_TXT (
set "LINE_TXT=!LINE_TXT:~%COL_FROM%,%COL_UPTO%!"
)
goto :NEXT
)
)
:NEXT
endlocal & set "LINE_TXT=%LINE_TXT%"
echo."%LINE_TXT%"
Both of the above code snippets rely on the output of findstr /N /R ".*", which returns every line that matches the regular expression .*, meaning zero or more characters, which in turn is actually true for every line in the file; however, the switch /N defines to prefix each line with its line number, which I extract and compare with the originally defined one.
Here is another variant which uses for /F to directly loop through the content (lines) of the given text file, without using findstr:
#echo off
for /F "usebackq skip=3 eol== delims=" %%L in (
"list.txt"
) do (
set "LINE_TXT=%%L"
goto :NEXT
)
:NEXT
if defined LINE_TXT set "LINE_TXT=%LINE_TXT:~20,29%"
echo."%LINE_TXT%"
This method has got the better performance, because there is the skip option which skips parsing of and iterating through all lines (1 to 3) before the line of interest (4), opposed to the findstring variant.
However, there is one disadvantage:
for /F features an eol option which defines a character interpreted as line comment (and defaults to ;); there is no way to switch this option off as long as delims= defines no delimiters (last position in option string), which is mandatory here to return the line as is; so you have to find a character that does not appear as the first one in any line (I defined = here because your sample text file uses this as header/footer character only).
To extract a string from line 1, remove the skip option as skip=0 results in a syntax error.
Note that goto :NEXT is required here; otherwise, the last (non-empty) line of the file is extracted.
Although for /F does not iterate any empty lines in the file, this is no problem here as the skip option does not check the line content and skip over empty lines as well.
Finally, here is one more approach using more +3 where no text parsing is done. However, a temporary file is needed here to pass the text of the desired line to the variable LINE_TXT:
#echo off
set LINE_TXT=
more +3 "list.txt" > "list.tmp"
set /P LINE_TXT= < "list.tmp"
del /Q "list.tmp"
if defined LINE_TXT set "LINE_TXT=%LINE_TXT:~20,29%"
echo."%LINE_TXT%"
exit /B 0
This method avoids for /F and therefore the problem with the unwanted eol option as mentioned in the above solution. But this does not handle tabs correctly as more substitutes them with spaces (8 indent spaces as per default and configurable by the /Tn switch where n is the number of spaces).
I have multiple TraceRT log files containing 30 hops. I'm only looking for similar IP (ex. 192.168.1) and would like to log it on one file with:
1) Successful: %IP% found in %Filename%
2) Fail: Specified IP not found in %Filename%
I'm trying to use:
rem************************************************************
:START
# ECHO OFF
rem US date
set YEAR=%DATE:~10,4%
set MONTH=%DATE:~4,2%
set DAY=%DATE:~7,2%
rem US hour
set HOUR=%TIME:~0,2%
set MIN=%TIME:~3,2%
set SEC=%TIME:~6,2%
set HUNDREDS=%TIME:~9,2%
set HOURMIN=%HOUR%%MIN%
rem Make sure that hour has two digits
IF %HOUR% GEQ 10 goto twoh
set HOUR1=%TIME:~1,1%
set TWOHOUR=0%HOUR1%
goto fulltid
:twoh
set TWOHOUR=%HOUR%
:fulltid
set FULLTIME=%TWOHOUR%'%MIN%'%SEC%'%HUNDREDS%
set FTIME=%TWOHOUR%:%MIN%:%SEC%
#echo off & setLocal EnableDELAYedeXpansion
findstr /m "192.168.1" *.txt > FILENAME
echo on
for /f "tokens=*" %%a in (*.txt ^| find "192.168.1") do (
IF %%a neq %%b (
echo Suscessful: %%a %FILENAME% >> Log%YEAR%%MONTH%%DAY%.txt
) ELSE (
echo Fail: Specified IP not found in %FILENAME% >> Log%YEAR%%MONTH%%DAY%.txt
)
)
goto START
rem************************************************************
You have specified an invalid pipe | find. You cannot pipe (a) text file(s) into a command.
Either provide the file(s) as argument(s) to find, or use redirection (this works for a single file only but not for multiple ones nor */? patterns though).
You are using for /f not correctly.
It looks as if you wanted to parse the output of find. To accomplish that, but you must enclose the command within single-quotes '. Type for /? and see the help text for more details.
The following line of code should work:
for /f "tokens=*" %%a in ('find "192.168.1" *.txt') do (
To get current date and time, I strongly recommend to read variables %DATE% and %TIME% once only and within a single line! Otherwise you might run into problems, especially concerning the fractional seconds, which might not be equal between consecutive expansions.
To ensure %HOUR% to have two digits, you simply need to use set HOUR=0%HOUR% then set HOUR=%HOUR:~-2%.
Since a one-digit %HOUR% is prefixed by a space here, you can have it even simpler (thanks for your comment, #Stephan!) by just replacing the space by a zero: set HOUR=%HOUR: =0%.