Reputation: 11
In a large .ahk file, i need to locate the text 'no label' in a line, delete 2 lines before that, delete 23 lines after that, and delete the line with 'no label' itself (26 lines total). There can be multiple cases of this in the file, and in all cases it needs to remove these 26 lines.
I have no knowledge of SED, AWK and so on, and I need this to run on a windows machine. Is it doable with a .bat or some other windows application that I'll be able to run?
Upvotes: 0
Views: 1411
Reputation: 80033
@ECHO OFF
SETLOCAL
SET "$="
SET "tempfile=%temp%\some-tempfile-name.txt"
ECHO.>"%tempfile%"
FOR /f "tokens=1*delims=:" %%a IN ('findstr /n /r ".*" q34397055.txt') DO (
ECHO %%b|FIND "no label" >NUL
IF NOT ERRORLEVEL 1 CALL :saveline# %%a
)
IF NOT DEFINED $ COPY /b q34397055.txt u:\newfile.txt 2>NUL >nul&GOTO done
(
FOR /f "tokens=1*delims=:" %%a IN ('findstr /n /r ".*" q34397055.txt^|findstr /v /b /g:"%tempfile%"') DO (
ECHO(%%b
)
)>u:\newfile.txt
:done
DEL "%tempfile%"
GOTO :EOF
:saveline#
:: calculate START line number to delete
SET /a $=%1 - 2
:: number of lines to delete
SETLOCAL enabledelayedexpansion
FOR /l %%d IN (1,1,26) DO (
>>"%tempfile%" ECHO(!$!:
SET /a $+=1
)
GOTO :EOF
I used a file named q34397055.txt
containing some test data for my testing.
Produces u:\newfile.txt
Essentially, read the entire file, numbering each line in the format line#:line content
Use the tokens
facility to extract the line number part, and if the line-content part contains the target string (it's not clear whether OP wants a line containing "no label" or whether a line exactly matching "no label" is required) then call :saveline#
passing the line number.
In :saveline#
, calculate the starting line of the block-to-be-deleted and then write the line numbers to be deleted to a file in the format (eg) 6:
..32:
.
Then perform the same numbering trick, but this time filter the output for lines that do not contain (/v
) at the beginning of the line (/b
) any string in the tempfile of line-numbers-to-be-deleted.
Output any line-content parts that pass through the filter.
[Edit : to fix empty-output-if-no-target-found problem
Insert set "$="
to ensure variable $
is deleted at the start.
Insert if not defined $...
line to detect whether $
has been established (ie. :saveline#
has been called at least once). Simply mechanically copy the source file to the destination if :saveline#
has not been called, and then skip to the label done
to delete the tempfile.
Insert the label done
Suggestion : establish variables to contain the source and destination filenames so that only one change need be made to change the filenames, not two or three. ]
Upvotes: 1
Reputation: 67216
@echo off
setlocal EnableDelayedExpansion
rem Get number of lines of sections to preserve in "copy.tmp" file
set last=0
(for /F "delims=:" %%a in ('findstr /N /C:"no label" input.ahk') do (
set /A copy=%%a-3-last, last=copy+26
echo !copy!
)) > copy.tmp
rem Read from input file
< input.ahk (
rem Process all "copy-lines, skip-26" sections
for /F %%n in (copy.tmp) do (
for /L %%i in (1,1,%%n) do (
set "line="
set /P "line="
echo(!line!
)
for /L %%i in (1,1,26) do set /P "line="
)
rem Copy the rest of lines after last section
findstr "^"
) > output.ahk
del copy.tmp
move /Y output.ahk input.ahk
Upvotes: 0
Reputation:
Okay, challenge accepted. This should do what you want in 100% batch-file form. I threw in a few comments but if you have questions feel free to ask.
First it scans the file for any instance of the searchPhrase
(currently "no label"). If found it saves that line number as refLine
*. It then sets the upper bounds as refLine
+ 23 and the lower bounds as refLine
- 2, as per your criteria. If the current line number falls outside those bounds it will write the line to a new, temporary, file. Once complete, it backs up the original file then deletes it and renames the temp file.
@echo off
setlocal enabledelayedexpansion
set "sourceFile=c:\temp\batchtest\myfile.txt"
set "tempFile=c:\temp\batchtest\tempfile.txt"
set "searchPhrase=no label"
set /a lineNum=0
REM check file for search phrase, store line as refLine
FOR /F "delims=" %%i IN (%sourceFile%) DO (
set /a lineNum+=1
echo !lineNum! = "%%i"
if "%%i" == "%searchPhrase%" (
echo Found "%searchPhrase%" on line !lineNum!
set /a refLine=!lineNum!
)
)
REM make backup
copy "%sourceFile%" "%sourceFile%-%DATE:/=-% %TIME::=-%.bak"
echo. 2>%tempFile%
REM Rewrite file
set /a lineNum=0
set /a lowEnd=%refLine%-2
echo "Set low end to %lowEnd%"
set /a highEnd=%refLine%+23
echo "Set high end to %highEnd%"
FOR /F "delims=" %%i IN (%sourceFile%) DO (
set /a lineNum+=1
if !lineNum! GTR %lowEnd% (
if !lineNum! LSS %highEnd% (
echo "Skipping line #!lineNum!"
)
)
if !lineNum! LSS %lowEnd% (
echo "Writing Line !lineNum! %%i to temp file..."
echo %%i >> %tempFile%
)
if !lineNum! GTR %highEnd% (
echo "Writing Line !lineNum! %%i to temp file..."
echo %%i >> %tempFile%
)
)
REM get target filename only
for %%F in ("%sourceFile%") do set fname=%%~nxF
REM del original file and rename tempfile
echo "Deleting original file..."
echo Y | del "%sourceFile%"
echo "Renaming %tempFile% to %fname%"
ren "%tempFile%" "%fname%"
*Note that it will currently only find one instance of "no label". If you think there are multiple instances, just run the bat file again. If a person wanted to, they could find multiple instances and store the line numbers to a 3rd, temporary, text file then use that to determine more complicated bounds for filtering. Alternatively, you could put a loop around the entire thing and exit the loop when it doesn't find an instance of the searchPhrase
.
Upvotes: 0