I have a txt file with this format:
some text
another uninteresting line
// some more lines can come here
[ actually interesting
// this is the stuff I want
]
I want to be able to get everything between the square brackets [] (including the brackets themselves).
(since I know that there's no text after the closing bracket, it's enough to be able to delete just the first lines before the [ char).
I'm pretty sure I can do it with findStr, but not sure exactly how.
You can use VBScript. Save the following as extract.vbs
flag=0
Do While Not WScript.StdIn.AtEndOfStream
Line = WScript.StdIn.ReadLine()
If Left(Line,1)="[" Then flag=1 End If
If flag=1 Then
WScript.Stdout.WriteLine(Line)
End If
Loop
Then run
CSCRIPT /NOLOGO EXTRACT.VBS < YOURFILE
It sets a flag to zero, then reads the input file one line at a time till the end. If it encounters a line starting with "[" it sets flag=1. Then it prints every line it finds when flag is set to 1.
If you want to save the lines it finds, in a new file, run it like this:
CSCRIPT /NOLOGO EXTRACT.VBS < YOURFILE > NEWFILE
FINDSTR cannot solve this on its own.
Given your situation that you can simply delete all lines before the line that starts with [, all you need is the following native batch script.
#echo off
setlocal
for /f "delims=:" %%N in ('findstr /n [ "file.txt"') do if not defined N set /a N=%%N-1
set "skip="
if %N% gtr 1 set "skip=skip=%N%"
(for /f "usebackq %skip% delims=" %%A in ("file.txt") do echo %%A) >"newFile.txt"
If you know that your file does not contain tabs, or if it is OK that tabs are converted to a string of spaces, then it is even easier:
#echo off
setlocal
for /f "delims=:" %%N in ('findstr /n [ "file.txt"') do if not defined N set /a N=%%N-1
more +%N% "file.txt" >"newFile.txt"
The solution is a one liner if you use REPL.BAT - a hybrid JScript/batch utility that performs regular expression search and replace on stdin and writes the result to std out. It is pure script that will run natively on any modern Windows machine from XP onward.
Assuming that [ only appears once, then:
type "file.txt" | repl "[^[]*\[" "[" m >"newFile.txt"
It is even simple to support multiple blocks between square brackets where the [ and/or ] could be in the middle of a line:
type "file.txt" | repl "[^[]*(\[[\s\S]*?\])[^[]*" "$1\r\n" mx >"newFile.txt"
#echo off
setlocal enableextensions disabledelayedexpansion
set "dataFile=data.txt"
rem search the starting line
set "startLine="
for /f "tokens=1 delims=:" %%a in (
'findstr /l /b /n /c:"[" "%dataFile%"'
) do if not defined startLine set "startLine=%%a"
rem remove all lines before the starting one
if defined startLine for /f "tokens=1,* delims=:" %%a in (
'findstr /n "^" "%dataFile%" ^& break ^> "%dataFile%"'
) do if %%a geq %startLine% >>"%dataFile%" echo(%%b
endlocal
If you install some tools from a proper Operating System (Unix/Linux) you can do it without any code:
grep -A 999 \[ yourfile
That says look for a [ character in yourfile and print it and up to 999 lines after (-A) it. Unix Utils are available for free here.
Related
This DOS batch script is stripping out the blank lines and not showing the blank lines in the file even though I am using the TYPE.exe command to convert the file to make sure the file is ASCII so that the FIND command is compatible with the file. Can anyone tell me how to make this script include blank lines?
#ECHO off
FOR /F "USEBACKQ tokens=*" %%A IN (`TYPE.exe "build.properties" ^| FIND.exe /V ""`) DO (
ECHO --%%A--
)
pause
That is the designed behavior of FOR /F - it never returns blank lines. The work around is to use FIND or FINDSTR to prefix the line with the line number. If you can guarantee no lines start with the line number delimiter, then you simply set the appropriate delimiter and keep tokens 1* but use only the 2nd token.
::preserve blank lines using FIND, assume no line starts with ]
::long lines are truncated
for /f "tokens=1* delims=]" %%A in ('type "file.txt" ^| find /n /v ""') do echo %%B
::preserve blank lines using FINDSTR, assume no line starts with :
::long lines > 8191 bytes are lost
for /f "tokens=1* delims=:" %%A in ('type "file.txt" ^| findstr /n "^"') do echo %%B
::FINDSTR variant that preserves long lines
type "file.txt" > "file.txt.tmp"
for /f "tokens=1* delims=:" %%A in ('findstr /n "^" "file.txt.tmp"') do echo %%B
del "file.txt.tmp"
I prefer FINDSTR - it is more reliable. For example, FIND can truncate long lines - FINDSTR does not as long as it reads directly from a file. FINDSTR does drop long lines when reading from stdin via pipe or redirection.
If the file may contain lines that start with the delimiter, then you need to preserve the entire line with the line number prefix, and then use search and replace to remove the line prefix. You probably want delayed expansion off when transferring the %%A to an environment variable, otherwise any ! will be corrupted. But later within the loop you need delayed expansion to do the search and replace.
::preserve blank lines using FIND, even if a line may start with ]
::long lines are truncated
for /f "delims=" %%A in ('type "file.txt" ^| find /n /v ""') do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*]=!"
echo(!ln!
endlocal
)
::preserve blank lines using FINDSTR, even if a line may start with :
::long lines >8191 bytes are truncated
for /f "delims=*" %%A in ('type "file.txt" ^| findstr /n "^"') do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
echo(!ln!
endlocal
)
::FINDSTR variant that preserves long lines
type "file.txt" >"file.txt.tmp"
for /f "delims=*" %%A in ('findstr /n "^" "file.txt.tmp"') do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
echo(!ln!
endlocal
)
del "file.txt.tmp"
If you don't need to worry about converting the file to ASCII, then it is more efficient to drop the pipe and let FIND or FINDSTR open the file specified as an argument, or via redirection.
There is another work around that completely bypasses FOR /F during the read process. It looks odd, but it is more efficient. There are no restrictions with using delayed expansion, but unfortunately it has other limitations.
1) lines must be terminated by <CR><LF> (this will not be a problem if you do the TYPE file conversion)
2) lines must be <= 1021 bytes long (disregarding the <CR><LF>)
3) any trailing control characters are stripped from each line.
4) it must read from a file - you can't use a pipe. So in your case you will need to use a temp file to do your to ASCII conversion.
setlocal enableDelayedExpansion
type "file.txt">"file.txt.tmp"
for /f %%N in ('find /c /v "" ^<"file.txt.tmp"') do set cnt=%%N
<"file.txt.tmp" (
for /l %%N in (1 1 %cnt%) do(
set "ln="
set /p "ln="
echo(!ln!
)
)
del "file.txt.tmp"
I wrote a very simple program that may serve as replacement for FIND and FINDSTR commands when they are used for this purpose. My program is called PIPE.COM and it just insert a blank space in empty lines, so all the lines may be directly processed by FOR command with no further adjustments (as long as the inserted space don't cares). Here it is:
#ECHO off
if not exist pipe.com call :DefinePipe
FOR /F "USEBACKQ delims=" %%A IN (`pipe ^< "build.properties"`) DO (
ECHO(--%%A--
)
pause
goto :EOF
:DefinePipe
setlocal DisableDelayedExpansion
set pipe=´)€ì!Í!ŠÐŠà€Ä!€ü.t2€ü+u!:æu8²A€ê!´#€ì!Í!².€ê!´#€ì!Í!²+€ê!´#€ì!Í!Šò€Æ!´,€ì!Í!"Àu°´LÍ!ëÒ
setlocal EnableDelayedExpansion
echo !pipe!>pipe.com
exit /B
EDIT: Addendum as answer to new comment
The code at :DefinePipe subroutine create a 88 bytes program called pipe.com, that basically do a process equivalent to this pseudo-Batch code:
set "space= "
set line=
:nextChar
rem Read just ONE character
set /PC char=
if %char% neq %NewLine% (
rem Join new char to current line
set line=%line%%char%
) else (
rem End of line detected
if defined line (
rem Show current line
echo %line%
set line=
) else (
rem Empty line: change it by one space
echo %space%
)
)
goto nextChar
This way, empty lines in the input file are changed by lines with one space, so FOR /F command not longer omit they. This works "as long as the inserted space don't cares" as I said in my answer.
Note that the pipe.com program does not work in 64-bits Windows versions.
Antonio
Output lines including blank lines
Here's a method I developed for my own use.
Save the code as a batch file say, SHOWALL.BAT and pass the source file as a command line parameter.
Output can be redirected or piped.
#echo off
for /f "tokens=1,* delims=]" %%a in ('find /n /v "" ^< "%~1"') do echo.%%ba
exit /b
EXAMPLES:
showall source.txt
showall source.txt >destination.txt
showall source.txt | FIND "string"
An oddity is the inclusion of the '^<' (redirection) as opposed to just doing the following:
for /f "tokens=1,* delims=]" %%a in ('find /n /v "" "%~1"') do echo.%%ba
By omitting the redirection, a leading blank line is output.
Thanks to dbenham, this works, although it is slightly different than his suggestion:
::preserve blank lines using FIND, no limitations
for /f "USEBACKQ delims=" %%A in (`type "file.properties" ^| find /V /N ""`) do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*]=!"
echo(!ln!
endlocal
)
As mentioned in this answer to the above question, it doesn't seem that lines are skipped by default using for /f in (at least) Windows XP (Community - Please update this answer by testing the below batch commands on your version & service pack of Windows).
EDIT: Per Jeb's comment below, it seems that the ping command, in at least Windows XP, is
causing for /f to produce <CR>'s instead of blank lines (If someone knows specifically why, would
appreciate it if they could update this answer or comment).
As a workaround, it seems that the second default delimited token (<space> / %%b in the example)
returns as blank, which worked for my situation of eliminating the blank lines by way of an "parent"
if conditional on the second token at the start of the for /f, like this:
for /f "tokens=1,2*" %%a in ('ping -n 1 google.com') do (
if not "x%%b"=="x" (
{do things with non-blank lines}
)
)
Using the below code:
#echo off
systeminfo | findstr /b /c:"OS Name" /c:"OS Version"
echo.&echo.
ping -n 1 google.com
echo.&echo.
for /f %%a in ('ping -n 1 google.com') do ( echo "%%a" )
echo.&echo.&echo --------------&echo.&echo.
find /?
echo.&echo.
for /f %%a in ('find /?') do ( echo "%%a" )
echo.&echo.
pause
.... the following is what I see on Windows XP, Windows 7 and Windows 2008, being the only three versions & service packs of Windows I have ready access to:
I have created command script for reading %N% lines from file. The problem is I can't delete " from anywhere in all text streams when I work with file's text. " deletion is very needed because if file's text line have substring like "text" and text have special chars or even worse, script code, then the script crashes or works not proper way (including script control capturing by programmer who specially composed the text).
If I can't delete " from the text stream(s), then I just want to identify, that the file (or it's first %N% lines, including empty lines) contains at least one " char.
Any thoughts are appreciated, including any file preprocessing. But main aim is script speed.
for /f "skip=2 delims=" %%a in ('find /v /n "" "file" 2^>nul') do set "v=%%a"&call :v&if not errorlevel 1 goto FURTHER1
goto FURTHER2
:v
for /f "delims=[]" %%a in ("%v%") do set "line%%a=%v:*]=%"&if %%a lss %N% (exit /b 1) else exit /b 0
#ECHO Off
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q39558311.txt"
SET "tempfilename1=%sourcedir%\q39558311#.txt"
>"%tempfilename1%" ECHO("
SET /a linefound=0
FOR /f "tokens=1 delims=:" %%a IN ('findstr /n /g:"%tempfilename1%" "%filename1%"') DO (
IF %%a gtr 2 SET /a linefound=%%a&GOTO report
)
:report
ECHO quote found AT line %linefound%
DEL "%tempfilename1%"
GOTO :EOF
You would need to change the setting of sourcedir and filename1 to suit your circumstances.
tempfile1 can be any name - it's just a temporary file; I chose that particular name for convenience.
I used a file named q39558311.txt containing some dummy data for my testing.
Essentially, create a file containing a single quote on a single line *tempfile1) then use findstr with the /g:filename option to read in the target strings to find. When findstr finds the line, it numbers it and outputs line_number:line found. Using : as a delimiter, token 1 of this line is the line number.
I don't understand why you've used the skip=number in your code. Do you intend to skip testing the first 2 lines of the target file?
the IF %%a gtr 2 tests the line number found. If it is greater than 2, then the variable linefound is set and the for loop is terminated.
I chose to initialise linefound to zero. It will remain zero if no " is found in lines 2..end. Equally, you could clear it and then it will be defined (with a value of first-line-found-with-quote-greater than-2) and no defined on not found.
I can only identify ", but not delete. Waiting for your suggestions on it!
>nul 2>&1 findstr /m \" "file"
if not errorlevel 1 echo double quote found!
i want to convert every whitespace in input to a new line character and save to a file (say tmp.txt) with batch file windows .
input:
http://www.example1.com wwwexample2com wwwexample3com wwwexample4com
to output in tmp.txt :
http://www.example1.com
wwwexample2com
wwwexample3com
wwwexample4com
how do I do that?
If the input is a file named "input file.txt" and the urls contain "=":
#echo off
setlocal enableDelayedExpansion
>"output file.txt" (for /f "usebackq delims=" %%a in ("input file.txt") do (
set line=%%a
call :listtokens "!line: =" "!"
))
pause & exit
:listtokens
if .%1==. (exit /b) else (echo %~1 & shift & goto listtokens)
If the input is a variable with alphanumeric words:
>"output file.txt" (for %%a in (%VARIABLE%) do echo %%a)
If the input is a file named "input file.txt" with alphanumeric words:
>"output file.txt" (for /f "usebackq delims=" %%a in ("input file.txt") do (
for %%b in (%%a) do echo %%b
))
If it's from a console utility that outputs alphanumeric words:
>"output file.txt" (for /f "delims=" %%a in ('run some command') do (
for %%b in (%%a) do echo %%b
))
The following pure batch script will put each space delimited "word" onto a separate line in the output. Empty lines will be stripped. The script is very robust. It should handle any ASCII text as long as every source line is <8kb. Note this will not convert other whitespace characters like tab into newlines.
#echo off
setlocal disableDelayedExpansion
>"input.txt.new" (
for /f "usebackq eol= tokens=* delims= " %%A in ("input.txt") do (
set "ln=%%A"
setlocal enableDelayedExpansion
echo(!ln: =^
!%= This line and the empty line above must remain exactly as written=%
endlocal
)
)
findstr . "input.txt.new" >"output.txt"
del "input.txt.new"
type output.txt
However, I would not use the above because I think pure batch makes text manipulation overly complex.
I would use a regular expression text processing utility called JREPL.BAT. It is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward. It is much simpler, faster, and more robust than any pure batch solution. The utility is extensively documented, and there are many regular expression tutorials available on the web.
The following one liner will efficiently put each whitespace delimtted "word" onto a separate line in the output.
jrepl "\S+" "$0" /jmatch /f "input.txt" /o "output.txt"
If you really want to convert every whitespace character into a newline, as you say in your comment, then you could use the following. But I don't think it is really what you want.
jrepl "\s" "\n" /x /f "input.txt" /o "output.txt"
Using a .bat script, I want to find a line that says # Site 1 and replace the text in the next line with a variable. I found tutorials on StackOverflow for finding and replacing a line, but not finding a line and replacing the next line. Any help?
#echo off
set "the_file=C:\someFile"
set "search_for=somestring"
set "variable=http://site1"
for /f "tokens=1 delims=:" %%# in ('findstr /n /c:"%search_for%" "%the_file%"') do (
set "line=%%#"
goto :break
)
:break
set /a lineBefore=line-1
set /a nextLine=line+1
break>"%temp%\empty"&&fc "%temp%\empty" "%the_file%" /lb %lineBefore% /t |more +4 | findstr /B /E /V "*****" >newFile
echo %variable%>>newFile
more "%the_file%" +%nextLine% 1>>newFile
echo move /y newFile "%the_file%"
Check if newFile is ok and remove the echo at the front of the last line.
you need to set the three variables at the beginning by yourself.Have on mind that more command sets spaces instead of tabs
#ECHO OFF
SETLOCAL
SET "filename=q28567045.txt"
SET "afterme=# Site 1"
SET "putme=put this line after # Site 1"
SET "skip1="
(
FOR /f "usebackqdelims=" %%a IN ("%filename%") DO (
IF DEFINED skip1 (ECHO(%putme%) ELSE (ECHO(%%a)
SET "skip1="
IF /i "%%a"=="%afterme%" SET skip1=y
)
)>newfile.txt
GOTO :EOF
Produces newfile.txt
The flag `skip1 to skip the line is first reset, then the file is read line by line.
If the skip1 flag is set, then the replacement line is echoed in place of the line read; if not, the line read is echoed.
Then the skip1 flag is cleared
If the line read to %%a matches the string assigned to afterme then the flag skip1 is set (to y - but it doesn't matter what the value is)
Note that empty lines and those starting ; will be ignored and not reproduced - this is standard behaviour of for /f.
If you want to replce the starting file, then simply add
move /y newfile.txt "%filename%"
before the goto :eof line.
Even though I enjoy working with batch, I generally avoid using pure native batch to edit text files because a robust solution is usually complicated and slow.
This can be done easily and efficiently using JREPL.BAT - a hybrid JScript/batch utility that performs regular expression replacement. JREPL.BAT is pure script that runs natively on any Windows machine from XP onward.
#echo off
setlocal
set "newVal=Replacement value"
call jrepl "^.*" "%newValue%" /jbeg "skip=true" /jendln "skip=($txt!='# Site 1')" /f test.txt /o -
The /F option specifies the file to process
The /O option with value of - specifies to replace the original file with the result.
The /JBEG option initializes the command to skip (not replace) each line.
The /JENDLN option checks the value of each line just before it is written out, and sets SKIP off (false) if it matches # Site 1. The next line will be replaced only when SKIP is false.
The search string matches an entire line.
The replacement string is your value stored in a variable.
This problem is similar to this one and may use an equivalent solution. The pure Batch file solution below should be the fastest one of its kind.
#echo off
setlocal EnableDelayedExpansion
set "search=# Site 1"
set "nextLine=Text that replaces next line"
rem Get the line number of the search line
for /F "delims=:" %%a in ('findstr /N /C:"%search%" input.txt') do set /A "numLines=%%a-1"
rem Open a code block to read-input-file/create-output-file
< input.txt (
rem Read the first line
set /P "line="
rem Copy numLines-1 lines
for /L %%i in (1,1,%numLines%) do set /P "line=!line!" & echo/
rem Replace the next line
echo %nextLine%
rem Copy the rest of lines
findstr "^"
) > output.txt
rem Replace input file with created output file
move /Y output.txt input.txt > NUL
This method will fail if the input file have empty lines and also have other limitations.
For a further description of this method, see this post.
I'm trying to insert a line into a file using the following code (from Write batch variable into specific line in a text file)
#echo off
setlocal enableextensions enabledelayedexpansion
set inputfile=variables.txt
set tempfile=%random%-%random%.tmp
copy /y nul %tempfile%
set line=0
for /f "delims=" %%l in (%inputfile%) do (
set /a line+=1
if !line!==4 (
echo WORDS YOU REPLACE IT WITH>>%tempfile%
) else (
echo %%l>>%tempfile%
)
)
del %inputfile%
ren %tempfile% %inputfile%
endlocal
My problem is the file has comment lines (which start with semicolons) which need to be kept
; directory during network startup. This statement must indicate a local disc
; drive on your PC and not a network disc drive.
LOCALDRIVE=C:\TEMP;
; PANELISATION PART/NET NAMING CONVENTION
; When jobs are panelised, parts/nets are renamed for each panel step by
When I run the batch file, it ignores the semicolon lines, So I only get:
LOCALDRIVE=C:\TEMP;
What do I need to do to keep the semicolon lines?
The EOL option determines what lines are to be ignored. The default value is a semicolon. If you know a character that can never appear in the first position of a line, then you can simply set EOL to that character. For example, if you know a line can't start with |, then you could use
for /f "eol=| delims=" %%l in (%inputfile%) do ...
There is an awkward syntax that disables EOL completely, and also disables DELIMS:
for /f delims^=^ eol^= %%l in (%inputfil%) do ...
Note that FOR /F always discards empty lines, so either of the above would result in:
; directory during network startup. This statement must indicate a local disc
; drive on your PC and not a network disc drive.
LOCALDRIVE=C:\TEMP;
; PANELISATION PART/NET NAMING CONVENTION
; When jobs are panelised, parts/nets are renamed for each panel step by
A trick is used if you want to preserve empty lines. Use FIND or FINDSTR to insert the line number before each line, and then use expansion find/replace to remove the line number. Now you know the line never begins with ;, so you can ignore the EOL option.
for /f "delims=" %%L in ('findstr /n "^" "%inputfile%"') do (
set "ln=%%L"
set "ln=!ln:*:=!"
REM You now have the original line, do whatever needs to be done here
)
But all of the above have a potential problem in that you have delayed expansion enabled when you expand the FOR variable, which means that any content containing ! will be corrupted. To solve this you must toggle delayed expansion on and off within the loop:
setlocal disableDelayedExpansion
...
for /f "delims=" %%L in (findstr /n "^" "%inputfile%") do (
set "ln=%%L"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
REM You now have the original line with ! preserved, do whatever needs done here
endlocal
)
Also, when ECHOing an empty line, it will print out ECHO is off unless you do something like
echo(!ln!
It takes time to open and position the write cursor to the end every time you use >> within the loop. It is faster to enclose the entire operation in one set of parentheses and redirect once. Also, you can replace the DEL and REN with a single MOVE command.
Here is a final robust script:
#echo off
setlocal disableDelayedExpansion
set "inputfile=variables.txt"
set line=0
>"%inputfile%.new" (
for /f "delims=" %%L in (findstr /n "^" "%inputfile%") do (
set "txt=%%L"
set /a line+=1
setlocal enableDelayedExpansion
set "txt=!txt:*:=!"
if !line! equ 4 (
echo New line content here
) else (
echo(!txt!
)
endlocal
)
)
move /y "%inputfile%.new" "%inputfile%" >nul
endlocal
That is an awful lot of work for such a simple task, and it requires a lot of arcane knowledge.
There is a much quicker hack that works as long as
your first 4 lines do not exceed 1021 bytes
none of your first 3 lines have trailing control characters that need to be preserved
the remaining lines do not have <tab> characters that must be preserved (MORE converts <tab> into a string of spaces.
#echo off
setlocal enableDelayedExpansion
set "inputfile=variables.txt"
>"%inputfile%.new" (
<"%inputfile%" (
for /l %%N in (1 1 3) do (
set "ln="
set /p "ln="
echo(!ln!
)
)
echo New line content here
more +4 "%inputfile%"
)
move /y "%inputfile%.new" "%inputfile%"
That is still a lot of work and arcane knowledge.
I would use my JREPL.BAT utility
Batch is really a terrible tool for text processing. That is why I developed JREPL.BAT to manipulate text using regular expressions. It is a hybrid JScript/batch script that runs natively on any Windows machine from XP onward. It is extremely versatile, robust, and fast.
A minimal amount of code is required to solve your problem with JREPL. Your problem doesn't really require the regular expression capabilities.
jrepl "^" "" /jendln "if (ln==4) $txt='New content here'" /f "variables.txt" /o -
If used within a batch script, then you must use call jrepl ... because JREPL.BAT is also a batch script.
By default, the FOR command treats ; as the end-of-line character, so all those lines that start with ; are being ignored.
Add eol= to your FOR command, like this:
for /f "eol= delims=" %%l in (%inputfile%) do (
It looks like you're echoing just the line delimiter, not the whole line:
echo %%l>>%tempfile%
I'm rusty on ms-dos scripts, so I can't give you more than that.