find_duplicate_lines.bat
Создаёт отчёт о дублях и очищенный UTF-8-файл, сохраняя порядок первых вхождений строк.
Поиск и очисткаПроверьте, содержит ли TXT-файл повторяющиеся строки, создайте очищенную копию без дублей с сохранением исходного порядка, устраните проблемы с кодировкой UTF-8 или выполните ручную проверку с помощью PowerShell и командной строки.
Самый удобный способ для регулярного использования — BAT-скрипт из способа 1. Перетащите на него TXT-файл, чтобы получить отчёт о дублях и очищенную копию без повторяющихся строк. Исходный файл не изменяется. Для быстрой проверки только в консоли используйте следующую команду PowerShell:
Get-Content -LiteralPath ".\input.txt" -Encoding UTF8 |
Group-Object -CaseSensitive |
Where-Object Count -gt 1 |
Sort-Object Count -Descending |
Select-Object Count, Name
Замените input.txt фактическим именем файла. Эта команда только выводит дубли и не создаёт очищенный файл. Параметр -Encoding UTF8 важен в Windows PowerShell 5.1, поскольку без него UTF-8-файл без метки порядка байтов может быть прочитан в системной кодировке Windows ANSI.
-CaseSensitive считает строки Server и server разными. Удалите этот параметр, если регистр букв нужно игнорировать.
РіС..., файл был прочитан в неправильной кодировке. Оставьте -Encoding UTF8 для файлов UTF-8. Используйте -Encoding Default для старого ANSI-файла или -Encoding Unicode для текста UTF-16 с порядком байтов Little Endian.
Создаёт отчёт о дублях и очищенный UTF-8-файл, сохраняя порядок первых вхождений строк.
Поиск и очисткаПоказывает каждую повторяющуюся строку и число её вхождений, но может медленно работать с очень большими файлами.
Быстрый отчётРазмещает одинаковые строки рядом, но не создаёт отчёт и не удаляет дубли автоматически.
Небольшие файлыПовторяющейся считается строка, значение которой встречается в одном файле более одного раза. Однако результат зависит от выбранных правил сравнения.
| Строки | Точное сравнение | Сравнение без учёта регистра |
|---|---|---|
Windows и Windows |
Дубль | Дубль |
Windows и windows |
Разные | Дубль |
example и example |
Разные из-за пробела в конце | Останутся разными, если не удалять пробелы |
| Две пустые строки | Повторяющаяся пустая строка | Повторяющаяся пустая строка |
Для строгой проверки учитывайте регистр и не удаляйте пробелы. При обработке списков имён, URL-адресов, доменов или идентификаторов может быть удобнее игнорировать регистр и случайные пробелы в начале или конце строки.
Для регулярных проверок используйте следующий find_duplicate_lines.bat. Перетащите на него текстовый файл или введите полный путь. Скрипт создаст в той же папке два файла UTF-8:
filename_duplicates.txt — отчёт, содержащий каждую повторяющуюся строку и общее число её вхождений.filename_without_duplicates.txt — очищенную копию, в которой сохраняется только первое вхождение каждой строки.find_duplicate_lines.bat в поле имени файла.@echo off
setlocal EnableExtensions DisableDelayedExpansion
title Find and Remove Duplicate Lines in a Text File
set "INPUT_FILE=%~1"
if not defined INPUT_FILE (
echo Drag a text file onto this BAT file, or enter its full path below.
echo.
set /p "INPUT_FILE=Text file path: "
)
if not defined INPUT_FILE (
echo.
echo No file was selected.
pause
exit /b 1
)
for %%I in ("%INPUT_FILE%") do set "INPUT_FILE=%%~fI"
if not exist "%INPUT_FILE%" (
echo.
echo File not found:
echo %INPUT_FILE%
pause
exit /b 1
)
for %%I in ("%INPUT_FILE%") do (
set "DUPLICATE_FILE=%%~dpnI_duplicates.txt"
set "CLEAN_FILE=%%~dpnI_without_duplicates.txt"
)
echo.
echo Processing:
echo %INPUT_FILE%
echo.
set "FD_SCRIPT_FILE=%~f0"
set "FD_INPUT_FILE=%INPUT_FILE%"
set "FD_DUPLICATE_FILE=%DUPLICATE_FILE%"
set "FD_CLEAN_FILE=%CLEAN_FILE%"
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$content=[IO.File]::ReadAllText($env:FD_SCRIPT_FILE); $marker=':'+'POWERSHELL'; $code=$content.Substring($content.IndexOf($marker)+$marker.Length); & ([ScriptBlock]::Create($code)) -Path $env:FD_INPUT_FILE -DuplicatePath $env:FD_DUPLICATE_FILE -CleanPath $env:FD_CLEAN_FILE"
set "RESULT=%ERRORLEVEL%"
echo.
if "%RESULT%"=="0" (
echo Finished. Two files were created:
echo Duplicate report:
echo %DUPLICATE_FILE%
echo.
echo File without duplicate lines:
echo %CLEAN_FILE%
) else if "%RESULT%"=="2" (
echo Finished. No exact duplicate lines were found.
echo The cleaned copy was still created:
echo %CLEAN_FILE%
) else (
echo The file could not be processed.
)
echo.
pause
exit /b %RESULT%
:POWERSHELL
param(
[Parameter(Mandatory = $true)]
[string]$Path,
[Parameter(Mandatory = $true)]
[string]$DuplicatePath,
[Parameter(Mandatory = $true)]
[string]$CleanPath
)
$ErrorActionPreference = 'Stop'
function Get-TextEncodingInfo {
param([string]$FilePath)
$stream = [System.IO.File]::OpenRead($FilePath)
try {
$bom = New-Object byte[] 4
$read = $stream.Read($bom, 0, 4)
}
finally {
$stream.Dispose()
}
if ($read -ge 4 -and
$bom[0] -eq 0x00 -and $bom[1] -eq 0x00 -and
$bom[2] -eq 0xFE -and $bom[3] -eq 0xFF) {
return [PSCustomObject]@{
Encoding = [System.Text.Encoding]::GetEncoding(12001)
AllowAnsiFallback = $false
}
}
if ($read -ge 4 -and
$bom[0] -eq 0xFF -and $bom[1] -eq 0xFE -and
$bom[2] -eq 0x00 -and $bom[3] -eq 0x00) {
return [PSCustomObject]@{
Encoding = [System.Text.Encoding]::UTF32
AllowAnsiFallback = $false
}
}
if ($read -ge 3 -and
$bom[0] -eq 0xEF -and $bom[1] -eq 0xBB -and
$bom[2] -eq 0xBF) {
return [PSCustomObject]@{
Encoding = New-Object System.Text.UTF8Encoding($true)
AllowAnsiFallback = $false
}
}
if ($read -ge 2 -and $bom[0] -eq 0xFF -and $bom[1] -eq 0xFE) {
return [PSCustomObject]@{
Encoding = [System.Text.Encoding]::Unicode
AllowAnsiFallback = $false
}
}
if ($read -ge 2 -and $bom[0] -eq 0xFE -and $bom[1] -eq 0xFF) {
return [PSCustomObject]@{
Encoding = [System.Text.Encoding]::BigEndianUnicode
AllowAnsiFallback = $false
}
}
return [PSCustomObject]@{
Encoding = New-Object System.Text.UTF8Encoding($false, $true)
AllowAnsiFallback = $true
}
}
function Read-And-CleanTextFile {
param(
[string]$FilePath,
[System.Text.Encoding]$Encoding,
[string]$TemporaryCleanPath
)
$comparer = [System.StringComparer]::Ordinal
$counts = New-Object 'System.Collections.Generic.Dictionary[string,int]' ($comparer)
$seen = New-Object 'System.Collections.Generic.HashSet[string]' ($comparer)
$utf8WithBom = New-Object System.Text.UTF8Encoding($true)
$reader = New-Object System.IO.StreamReader($FilePath, $Encoding, $true)
$writer = New-Object System.IO.StreamWriter($TemporaryCleanPath, $false, $utf8WithBom)
$totalLines = 0
try {
while (($line = $reader.ReadLine()) -ne $null) {
$totalLines++
if ($seen.Add($line)) {
$writer.WriteLine($line)
}
$count = 0
if ($counts.TryGetValue($line, [ref]$count)) {
$counts[$line] = $count + 1
}
else {
$counts.Add($line, 1)
}
}
}
finally {
$reader.Dispose()
$writer.Dispose()
}
return [PSCustomObject]@{
Counts = $counts
TotalLines = $totalLines
UniqueLines = $seen.Count
}
}
$tempCleanPath = Join-Path ([System.IO.Path]::GetDirectoryName($CleanPath)) ([System.IO.Path]::GetRandomFileName())
try {
$encodingInfo = Get-TextEncodingInfo -FilePath $Path
try {
$data = Read-And-CleanTextFile -FilePath $Path -Encoding $encodingInfo.Encoding -TemporaryCleanPath $tempCleanPath
}
catch [System.Text.DecoderFallbackException] {
if (-not $encodingInfo.AllowAnsiFallback) {
throw
}
if (Test-Path -LiteralPath $tempCleanPath) {
Remove-Item -LiteralPath $tempCleanPath -Force
}
$data = Read-And-CleanTextFile -FilePath $Path -Encoding ([System.Text.Encoding]::Default) -TemporaryCleanPath $tempCleanPath
}
if (Test-Path -LiteralPath $CleanPath) {
Remove-Item -LiteralPath $CleanPath -Force
}
Move-Item -LiteralPath $tempCleanPath -Destination $CleanPath
$duplicates = @(
$data.Counts.GetEnumerator() |
Where-Object { $_.Value -gt 1 } |
Sort-Object Key
)
if ($duplicates.Count -eq 0) {
if (Test-Path -LiteralPath $DuplicatePath) {
Remove-Item -LiteralPath $DuplicatePath -Force
}
Write-Host ("Lines read: {0}" -f $data.TotalLines)
Write-Host ("Unique lines written: {0}" -f $data.UniqueLines)
exit 2
}
$report = New-Object System.Collections.Generic.List[string]
$report.Add("Count`tLine")
foreach ($duplicate in $duplicates) {
$report.Add(("{0}`t{1}" -f $duplicate.Value, $duplicate.Key))
}
$utf8WithBom = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllLines($DuplicatePath, $report, $utf8WithBom)
Write-Host ("Lines read: {0}" -f $data.TotalLines)
Write-Host ("Unique lines written: {0}" -f $data.UniqueLines)
Write-Host ("Duplicate groups found: {0}" -f $duplicates.Count)
exit 0
}
catch {
if (Test-Path -LiteralPath $tempCleanPath) {
Remove-Item -LiteralPath $tempCleanPath -Force
}
Write-Error $_.Exception.Message
exit 1
}
find_duplicate_lines.bat. Также можно дважды щёлкнуть BAT-файл и вставить полный путь к текстовому файлу.filename_duplicates.txt, чтобы просмотреть повторяющиеся строки и количество их вхождений.filename_without_duplicates.txt, чтобы получить очищенный список, из которого удалены повторные вхождения.| Файл | Порядок сортировки | Содержимое |
|---|---|---|
filename_duplicates.txt |
Сортируется по алфавиту по тексту повторяющейся строки | Одна запись для каждого повторяющегося значения с общим числом вхождений |
filename_without_duplicates.txt |
Не сортируется; исходный порядок сохраняется | Только первое вхождение каждой точно совпадающей строки |
| Исходный TXT-файл | Не сортируется и не изменяется | Остаётся без изменений |
Count Line
4 example.com
2 server-01
Windows и windows считаются разными строками.PowerShell читает файл как набор строк. -Encoding UTF8 указывает Windows PowerShell 5.1 правильно декодировать UTF-8-файл, включая UTF-8 без метки порядка байтов. Group-Object группирует одинаковые строки, а Where-Object оставляет только группы, содержащие более одного элемента.
powershell и нажмите Enter.input.txt именем своего файла.Get-Content -LiteralPath ".\input.txt" -Encoding UTF8 |
Group-Object -CaseSensitive |
Where-Object Count -gt 1 |
Sort-Object Count -Descending |
Select-Object Count, Name
Count Name
----- ----
4 example.com
3 192.168.1.10
2 Windows 11
Этот результат означает, что строка example.com встречается четыре раза, 192.168.1.10 встречается три раза, а Windows 11 встречается два раза.
Эту же проверку можно запустить из любой папки, указав полный путь:
Get-Content -LiteralPath "C:\Users\User\Desktop\input.txt" -Encoding UTF8 |
Group-Object -CaseSensitive |
Where-Object Count -gt 1 |
Select-Object Count, Name
-LiteralPath предпочтительнее для имён файлов с такими символами, как квадратные скобки, поскольку PowerShell обрабатывает путь буквально. В примерах используется -Encoding UTF8; если исходный файл имеет другую кодировку, укажите соответствующий параметр.
Используйте этот вариант, если нужно лишь узнать, существует ли хотя бы одна повторяющаяся строка:
$duplicate = Get-Content -LiteralPath ".\input.txt" -Encoding UTF8 |
Group-Object -CaseSensitive |
Where-Object Count -gt 1 |
Select-Object -First 1
if ($null -ne $duplicate) {
Write-Host "Duplicate lines found."
} else {
Write-Host "No duplicate lines found."
}
Команда возвращает только первую группу дублей, однако PowerShell всё равно должен прочитать и сгруппировать файл перед выводом результата. Этот вариант удобен в скрипте, запланированной задаче или регулярно выполняемой проверке.
if (Get-Content -LiteralPath ".\input.txt" -Encoding UTF8 | Group-Object -CaseSensitive | Where-Object Count -gt 1 | Select-Object -First 1) { "Duplicate lines found" } else { "No duplicate lines found" }
Чтобы сохранить отчёт о дублях, а не только вывести его в консоли, экспортируйте сгруппированные результаты в CSV-файл:
Get-Content -LiteralPath ".\input.txt" -Encoding UTF8 |
Group-Object -CaseSensitive |
Where-Object Count -gt 1 |
Sort-Object Count -Descending |
Select-Object Count, @{Name="Line"; Expression={$_.Name}} |
Export-Csv -LiteralPath ".\duplicate-report.csv" -NoTypeInformation -Encoding UTF8
Выходной файл duplicate-report.csv можно открыть в Excel, LibreOffice Calc, Блокноте или другом текстовом редакторе.
Используйте следующую команду, если нужен обычный TXT-файл, содержащий по одной копии каждой строки, которая повторялась:
Get-Content -LiteralPath ".\input.txt" -Encoding UTF8 |
Group-Object -CaseSensitive |
Where-Object Count -gt 1 |
ForEach-Object Name |
Set-Content -LiteralPath ".\duplicate-lines.txt" -Encoding UTF8
Set-Content, пока тот же конвейер продолжает читать этот файл.
Group-Object по умолчанию не учитывает регистр. Удалите -CaseSensitive, чтобы считать PC, Pc и pc одинаковыми значениями:
Get-Content -LiteralPath ".\input.txt" -Encoding UTF8 |
Group-Object |
Where-Object Count -gt 1 |
Select-Object Count, Name
Get-Content -LiteralPath ".\input.txt" -Encoding UTF8 |
ForEach-Object { $_.Trim() } |
Group-Object -CaseSensitive |
Where-Object Count -gt 1 |
Select-Object Count, Name
В результате строки example, example и example будут считаться одинаковыми. Пробелы внутри строки не удаляются.
Get-Content -LiteralPath ".\input.txt" -Encoding UTF8 |
Where-Object { $_.Trim().Length -gt 0 } |
Group-Object -CaseSensitive |
Where-Object Count -gt 1 |
Select-Object Count, Name
Get-Content -LiteralPath ".\input.txt" -Encoding UTF8 |
ForEach-Object { $_.Trim() } |
Where-Object { $_.Length -gt 0 } |
Group-Object |
Where-Object Count -gt 1 |
Sort-Object Count -Descending |
Select-Object Count, Name
Встроенная в Windows команда sort.exe может расположить одинаковые строки рядом. Это упрощает ручной поиск дублей, однако SORT не определяет, не подсчитывает и не удаляет повторяющиеся строки автоматически.
cmd и нажмите Enter.sort "input.txt" /o "sorted.txt"
Откройте sorted.txt в Блокноте. Повторяющиеся значения будут расположены рядом, что позволит проверить их визуально.
Для автоматической проверки из командной строки запустите Windows PowerShell через CMD. В этом примере UTF-8 указана явно, чтобы Windows PowerShell 5.1 не декодировал кириллицу и символы с диакритикой как ANSI:
powershell.exe -NoProfile -Command "Get-Content -LiteralPath '.\input.txt' -Encoding UTF8 | Group-Object -CaseSensitive | Where-Object Count -gt 1 | Select-Object Count, Name"
BAT-скрипт из способа 1 эффективнее коротких примеров с Group-Object, поскольку не считывает весь файл в массив и не сортирует каждую исходную строку по алфавиту. Он читает файл последовательно, сразу записывает первые вхождения в очищенный файл и хранит в памяти только уникальные ключи и их счётчики.
Прямые команды PowerShell остаются удобными, но Group-Object может работать заметно медленнее и потреблять много оперативной памяти при обработке большого файла.
При работе с большим файлом соблюдайте следующие рекомендации:
Group-Object используйте потоковый скрипт или процесс на основе базы данных.Если PowerShell отображает кириллицу, символы с диакритикой или другой неанглийский текст в виде последовательностей вроде РіС..., исходный файл, скорее всего, декодируется в неправильной кодировке. Сам текст может оставаться неповреждённым; проблема заключается в том, как Get-Content интерпретирует байты файла.
-Encoding UTF8.
Get-Content -LiteralPath ".\input.txt" -Encoding UTF8 |
Group-Object -CaseSensitive |
Where-Object Count -gt 1 |
Sort-Object Count -Descending |
Select-Object Count, Name
В Windows PowerShell 5.1 это работает для текста UTF-8 как с меткой порядка байтов, так и без неё. В новых версиях PowerShell UTF-8 обычно используется по умолчанию для многих текстовых операций, но явное указание кодировки делает команду однозначной.
| Кодировка исходного файла | Параметр Get-Content | Когда использовать |
|---|---|---|
| UTF-8 | -Encoding UTF8 |
Рекомендуется для современных TXT-файлов, включая UTF-8 без BOM. |
| Кодовая страница Windows ANSI | -Encoding Default |
Используйте для старых файлов, сохранённых в текущей системной кодовой странице Windows. |
| UTF-16 Little Endian | -Encoding Unicode |
Используйте для файлов UTF-16 LE, которые часто можно определить по FF FE BOM. |
| UTF-16 Big Endian | -Encoding BigEndianUnicode |
Используйте для файлов UTF-16 BE, которые часто можно определить по FE FF BOM. |
Get-Content -LiteralPath ".\input.txt" -Encoding Default |
Group-Object -CaseSensitive |
Where-Object Count -gt 1 |
Select-Object Count, Name
Get-Content -LiteralPath ".\input.txt" -Encoding Unicode |
Group-Object -CaseSensitive |
Where-Object Count -gt 1 |
Select-Object Count, Name
chcp 65001 изменяет кодовую страницу консоли, но не сообщает Get-Content, как декодировать исходный файл. Вместо этого укажите -Encoding UTF8 для Get-Content.
Повторно используемый find_duplicate_lines.bat из этого руководства распознаёт UTF-8 с BOM и без BOM, поддерживает UTF-16 и UTF-32 с BOM и использует кодовую страницу Windows ANSI, если последовательность байтов без BOM не является корректным UTF-8. И отчёт о дублях, и очищенная копия сохраняются в UTF-8 с BOM.
Get-Content, Group-Object и Select-Object только читают и анализируют файл, если явно не добавить команду записи, например Set-Content или Export-Csv.(Get-Content -LiteralPath ".\input.txt" -Encoding UTF8).Count. При обработке очень большого файла PowerShell всё равно должен прочитать его, поэтому операция может занять время.find_duplicate_lines.bat. Скрипт автоматически создаёт *_without_duplicates.txt и не изменяет исходный файл. В очищенном результате сохраняется первое вхождение каждой точно совпадающей строки.Group-Object по умолчанию не учитывает регистр. Добавьте -CaseSensitive, если прописные и строчные буквы нужно сравнивать точно.-Encoding UTF8 сразу после пути к файлу. Изменение кодовой страницы консоли командой chcp 65001 само по себе не исправляет то, как Get-Content декодирует файл.find_duplicate_lines.bat читает выбранный файл и создаёт *_duplicates.txt и *_without_duplicates.txt. Исходный текстовый файл никогда не перезаписывается.Для регулярных проверок используйте find_duplicate_lines.bat. Он принимает файл перетаскиванием, выполняет точное сравнение с учётом регистра, создаёт отсортированный по алфавиту отчёт о дублях и записывает второй UTF-8-файл без повторных вхождений, сохраняя исходный порядок.
Используйте PowerShell с Get-Content -Encoding UTF8 и Group-Object, если нужен только интерактивный отчёт или требуется изменить правила сравнения. Команда Windows sort.exe подходит только для ручной проверки небольших файлов.