Skip to content

How to Delete a Specific Line Number from a File in Batch Script

Learn three reliable methods to delete a specific line by number in a file using native Batch, FINDSTR pattern matching, or PowerShell for efficient large-file processing.

Programmatically removing a line from a file (for example, deleting a revoked user from a permissions list or removing an old entry from a static routing table) is a core part of system maintenance. Since Batch cannot delete a line in place, we must filter the file: read the original and write everything except the targeted line to a new file.

In this guide, we will demonstrate how to delete a specific line using standard Batch techniques and PowerShell bridging.

Method 1: The "Skip" Loop (Native Batch)

This method iterates through the file and compares the current line number to your target. To ensure we do not accidentally delete empty lines or lines starting with semicolons (a common issue in Batch), we pipe the file through findstr /n, which numbers every line before we process it.

Implementation Script

@echo off
setlocal disabledelayedexpansion

set "Source=UserList.txt"
set "TempFile=%TEMP%\del_line_%RANDOM%.tmp"
set "TargetLine=5"

:: Verify source file exists
if not exist "%Source%" (
    echo [ERROR] Source file "%Source%" not found.
    pause
    exit /b 1
)

:: Validate that TargetLine is a positive number
if %TargetLine% lss 1 (
    echo [ERROR] Line number must be 1 or greater.
    pause
    exit /b 1
)

:: Check if the target line is within the file's bounds
for /f %%C in ('find /c /v "" ^< "%Source%"') do set "TotalLines=%%C"
if %TargetLine% gtr %TotalLines% (
    echo [WARNING] Line %TargetLine% exceeds file length ^(%TotalLines% lines^).
    echo           No changes were made.
    pause
    exit /b 1
)

echo Deleting line %TargetLine% from "%Source%"...

:: findstr /n "^" prefixes every line with "LineNumber:". 
:: This preserves blank lines and lines starting with special characters.
(
    for /f "delims=" %%A in ('findstr /n "^" "%Source%"') do (
        set "ln=%%A"
        setlocal enabledelayedexpansion

        :: Remove the line number and the first colon to get the exact original text
        set "line=!ln:*:=!"

        :: Extract just the line number into %%N
        for /f "delims=:" %%N in ("!ln!") do (
            :: If this is NOT our target line, write it to the output
            if %%N neq %TargetLine% (
                echo(!line!
            )
        )
        endlocal
    )
) > "%TempFile%"

:: Replace original with the filtered version
move /y "%TempFile%" "%Source%" >nul

if %errorlevel% equ 0 (
    echo [SUCCESS] Line %TargetLine% removed from "%Source%".
) else (
    echo [ERROR] Failed to replace original file.
    pause
    exit /b 1
)
pause
exit /b 0

Method 2: The "Pattern" Deletion (FINDSTR)

If you do not know the exact line number but you know the content of the line, you can use findstr with the /V (invert) switch to exclude it.

@echo off
setlocal

set "Source=users.txt"
set "Dest=users_cleaned.txt"
set "Pattern=REVOKED_USER"

:: Verify source file exists
if not exist "%Source%" (
    echo [ERROR] Source file "%Source%" not found.
    pause
    exit /b 1
)

echo Removing lines containing "%Pattern%" from "%Source%"...

:: /V outputs lines that do NOT match the pattern
:: /C: treats the pattern as a literal string
findstr /V /C:"%Pattern%" "%Source%" > "%Dest%"

if %errorlevel% gtr 1 (
    echo [ERROR] Failed to process file (Syntax error or missing file^).
    pause
    exit /b 1
)

echo [SUCCESS] Filtered output saved to "%Dest%".
pause
exit /b 0

Native Batch loops are slow for massive text files. PowerShell handles line-number deletion efficiently using the RemoveAt() method, which avoids the array-slicing pitfalls of index arithmetic.

@echo off
setlocal

set "Source=log.txt"
set "TargetLine=5"

:: Verify source file exists
if not exist "%Source%" (
    echo [ERROR] Source file "%Source%" not found.
    pause
    exit /b 1
)

echo Deleting line %TargetLine% from "%Source%"...

:: ArrayList.RemoveAt() uses a zero-based index, so we subtract 1
:: Wrapping Get-Content in an ArrayList array forces it to handle 1-line files correctly
powershell -NoProfile -Command ^
    "$lines = [System.Collections.ArrayList]@(Get-Content -Path '%Source%'); " ^
    "$idx = %TargetLine% - 1; " ^
    "if ($idx -lt 0 -or $idx -ge $lines.Count) { " ^
    "  Write-Host '[WARNING] Line %TargetLine% is out of range.'; exit 1 " ^
    "} else { " ^
    "  $lines.RemoveAt($idx); " ^
    "  $lines | Set-Content -Path '%Source%' -Encoding UTF8 " ^
    "}"

if %errorlevel% equ 0 (
    echo [SUCCESS] Line %TargetLine% deleted from "%Source%".
) else (
    echo [WARNING] No changes were made.
    pause
    exit /b 1
)
pause
exit /b 0

Deleting Multiple Lines

To delete more than one line in a single pass, you can check the line number against a predefined list. Each target must be defined before the loop begins since the line numbers refer to the original file positions.

@echo off
setlocal disabledelayedexpansion

set "Source=TaskList.txt"
set "TempFile=%TEMP%\del_multi_%RANDOM%.tmp"

:: Define lines to delete (use delimiters to enable exact matching)
set "targets=|3|7|12|"

if not exist "%Source%" (
    echo [ERROR] Source file "%Source%" not found.
    pause
    exit /b 1
)

echo Deleting lines 3, 7, and 12 from "%Source%"...

(
    for /f "delims=" %%A in ('findstr /n "^" "%Source%"') do (
        set "ln=%%A"
        setlocal enabledelayedexpansion
        set "line=!ln:*:=!"

        :: Extract the line number into %%N
        for /f "delims=:" %%N in ("!ln!") do (
            :: Check if the current line number (%%N) exists in the targets string
            if "!targets:|%%N|=!" neq "!targets!" (
                rem This line is targeted for deletion - skip it
            ) else (
                echo(!line!
            )
        )
        endlocal
    )
) > "%TempFile%"

move /y "%TempFile%" "%Source%" >nul
echo [SUCCESS] Lines removed from "%Source%".
pause
exit /b 0
Navigation