Skip to main content

How to Head and Tail a File Simultaneously in Batch Script

When auditing massive log files, you usually care about two things: how the process started (the Head) and where it currently stands (the Tail). Displaying both at once gives you a complete context snapshot without forcing you to scroll through thousands of lines of middle content.

In this guide, we will demonstrate how to display the first and last few lines of a file simultaneously using native Batch commands.

Method 1: The "Combined View" Script

This script reads the file to count total lines, then makes a single pass to extract both the head and tail sections.

Implementation Script

@echo off
setlocal disabledelayedexpansion

set "target=SystemLog.txt"
set "linesToFetch=5"

:: Verify file exists and is not empty
if not exist "%target%" (
echo [ERROR] File "%target%" not found.
pause
exit /b 1
)

:: Count total lines in the file
for /f %%C in ('find /c /v "" ^< "%target%"') do set "total=%%C"

if %total% equ 0 (
echo [WARNING] File "%target%" is empty.
pause
exit /b 1
)

:: Calculate where the tail section begins
set /a "tailStart=total - linesToFetch"

:: If the file is short enough to show entirely, just display it all
if %total% leq %linesToFetch% (
set /a "tailStart=total"
) else (
:: If head and tail would overlap, adjust to avoid duplicating lines
set /a "doubleCount=linesToFetch * 2"
setlocal enabledelayedexpansion
if !doubleCount! geq %total% (
set /a "tailStart=linesToFetch"
)
endlocal
)

:: 1. Display Head
echo ==========================================
echo HEAD: FIRST %linesToFetch% LINES
echo ==========================================

set "count=0"
for /f "usebackq delims=" %%A in ("%target%") do (
set /a "count+=1"
set "line=%%A"
setlocal enabledelayedexpansion
if !count! leq %linesToFetch% (
echo [L:!count!] !line!
)
endlocal
set /a "check=count"
setlocal enabledelayedexpansion
if !check! geq %linesToFetch% if %tailStart% gtr %linesToFetch% (
goto :headDone
)
endlocal
)
:headDone

:: 2. Display separator if middle content was skipped
setlocal enabledelayedexpansion
if %tailStart% gtr %linesToFetch% (
echo.
echo [ ... %total% total lines - middle skipped ... ]
echo.
) else if %total% leq %linesToFetch% (
echo.
echo [ File contains only %total% lines - shown above ]
echo.
pause
exit /b 0
)
endlocal

:: 3. Display Tail
echo ==========================================
echo TAIL: LAST %linesToFetch% LINES
echo ==========================================

set "current=0"
for /f "usebackq delims=" %%B in ("%target%") do (
set /a "current+=1"
set "line=%%B"
setlocal enabledelayedexpansion
if !current! gtr %tailStart% (
echo [L:!current!] !line!
)
endlocal
)

echo ==========================================
pause
exit /b 0
warning

The for /f loop skips blank lines by design and also skips lines beginning with ; (the default eol character). If your log file contains blank lines, the total line count from find /c /v "" will include them but the for /f loop will not, causing the line numbers and tail position to be inaccurate. For files with blank lines, use the PowerShell method (Method 2) instead.

tip

The script uses the delayed expansion toggle pattern: each line is set with delayed expansion disabled (set "line=%%A") to preserve literal ! characters in the file content. It is then output with delayed expansion enabled (echo ... !line!) to safely handle &, |, >, <, and other special characters.

If your file is very large, the Batch loop is slow because it must read the entire file for the tail section. PowerShell's -Tail parameter reads from the end of the file without scanning all preceding lines.

Implementation Script

@echo off
setlocal

set "target=SystemLog.txt"
set "num=5"

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

:: Get total line count for context
for /f %%C in ('find /c /v "" ^< "%target%"') do set "total=%%C"

if %total% equ 0 (
echo [WARNING] File "%target%" is empty.
pause
exit /b 1
)

echo ==========================================
echo HEAD: FIRST %num% LINES
echo ==========================================
powershell -NoProfile -Command "Get-Content '%target%' -TotalCount %num%"

echo.
echo [ ... %total% total lines - middle skipped ... ]
echo.

echo ==========================================
echo TAIL: LAST %num% LINES
echo ==========================================
powershell -NoProfile -Command "Get-Content '%target%' -Tail %num%"

echo ==========================================
pause
exit /b 0
info

The -Tail parameter in PowerShell reads from the end of the file using a reverse seek, making it extremely fast even on multi-gigabyte log files. Unlike the Batch method, it does not need to iterate through every preceding line to reach the tail section. The -TotalCount parameter similarly stops reading after the specified number of lines.

Why Show Both at Once?

  1. Troubleshooting: You can verify that the configuration loaded correctly (head) and then immediately see why the script crashed hours later (tail).
  2. Visual Confirmation: In a large data migration, seeing the headers ensures the file format is correct, while seeing the tail confirms the process reached the end of the file.
  3. Dashboards: If you have an administrative dashboard, showing the start time and current status in one window provides the highest level of information density.

Best Practices

  1. Handle Empty and Short Files: Always check that the file exists and is not empty before running the head/tail logic. If the file has fewer lines than the requested count, display the entire file once rather than showing duplicate content in both sections.
  2. Prevent Overlap: If the head and tail sections would overlap (e.g., requesting 10 lines from each end of a 15-line file), detect this and adjust the tail start position so that no lines are displayed twice.
  3. Highlight the Junction: Use a visual separator like [ ... middle skipped ... ] with the total line count so the user understands they are not seeing the entire file and has a sense of its scale.
  4. Include Line Numbers: Showing that the tail starts at line 50,000 gives the user immediate context about the size of the file without needing to count.
  5. Choose the Right Method: Use Method 1 when PowerShell is unavailable or for small files. Use Method 2 for large files where the Batch loop would be unacceptably slow, especially for the tail section.

Conclusion

Displaying the head and tail of a file simultaneously is a powerful data-analysis technique that saves time and mental effort. By combining these two perspectives into a single console output, you create a high-resolution view of your logs and data sets, allowing you to bridge the gap between initial configuration and final execution results in seconds.