Тема: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле
Предислолвие
Windows не имеет нативных средств для работы с текстовыми файлами из командной строки. Как правильно сказал коллега greg zakharov в теме JScript: чтение N-последних строк файла
В Windows'е на командном языке можно соорудить нечто подобное, но камнем преткновения будут кодировки
Здесь хотелось бы заметить, что проблема с кодировками будет при написании скрипта, на любом из родных для системы языков - cmd, vbscript, jscript.
Можно только сожалеть об отсутствии и решить эту проблему одним из следующих способов:
1. установить один из пакетов, содержащих достаточный набор команд, чтобы быть также уверенным как в командной строке unix, например, UnixUtils, Cygwin, GnuWin32
Иногда это не возможен из-за политик компаний, запрещающих самостоятельно устанавливать дополнительное ПО.
2. использовать PowerShell
Хотя все идет к тому, что PowerShell будет предустановлен на всех Windows, однако еще много систем на которых его нет. А установить нельзя.
3. написать собственный скрипт
Это уже выход, если 1 и 2 противопоказаны. Хотя есть свои недостатки (например упомянутая проблема - кодировки).
Я решил попробовать (частично или полностью) эмулировать работу некоторых важных команд обработки текстовых файлов средствами самой командной строки. Just for fun!
.
1. Количество строк в файле
Оказывается почти точно определить количество строк в файле очень просто:
find /c /v "" file1 file2 ...
И вот пример. Данная команда выводит немного дополнительной информации
C:\>find /c /v "" c:\autoexec.bat
---------- C:\AUTOEXEC.BAT: 1
Сказав "почти точно", я имел в виду, то что иногда результаты отличаются от видимого. Но этим грешат многие команды. Например, wc -l отбрасывает последнюю строку, если она не заканчивается переводом строки. В случае с find /c /v "" наблюдается расхождение с действительностью, если в конце файла несколько (более 2) пустых строк. По-видимому разные программы работают по разному - одни считают количество строк, другие количество переводов строк. И вследствие этого они могут различаться. Поэтому я рекомендую использовать следующее решение.
more file | find /c /v ""
Команда выведет только размер файла в строках. И это самый точный способ узнать количество строк в файле.
Коли я упомянул в самом начале PowerShell, приведу примеры реализации с его помощью (я не знаток его синтаксиса и тонкостей, поэтому привожу некоторые, известные мне реализации, возможно не самые удачные, но полностью рабочие). Прелесть этих решений еще в том, что это однострочные команды (вики на англ):
:: Вычислить количество строк в файле
echo (get-content -path %windir%\System32\drivers\etc\hosts).length | powershell -command -
.
2. Прочитать n последних строк в файле. Эмуляция команды tail
Оказывается, это очень простая задача:
1. узнать N - количество строк в файле;
2. вывести n строк, пропустив N-n:
@echo off
:: Вывод n последних строк файла
::
:: Использование:
:: tail n filename
::
:: Пример:
:: tail 10 %windir%\System32\drivers\etc\hosts
setlocal enabledelayedexpansion
:: Узнать количество строк в файле
for /f %%n in ( 'more ^< "%~2" ^| find /c /v ""' ) do (
set tail.count=%%n
)
:: Вычислить сколько строк пропустить
set /a tail.count-=%~1
:: Вывести n последних строк из файла
more +!tail.count! < "%~2"
.
Код на PowerShell:
:: вывести 10 последних строк файла (2 варианта, первый быстрее)
echo (get-content -path %windir%\System32\drivers\etc\hosts)[-10..-1] | powershell -command -
echo "get-content -path %windir%\System32\drivers\etc\hosts | select-object -last 10" | powershell -command -
.
3. Вывод нескольких начальных строк файла. Эмуляция команды head
Этот эмулятор я написал первым, хотя он не самый простой в реализации. Возможно, существуют варианты лучше, но, к сожалению, я их не нашел и, поэтому, я использовал "хак" с временным файлом. Суть его в том, что вначале создается временный файл, который содержит n-ое количество следующих строк:
[1]
[2]
...
[n]
то есть все номера требуемых строк.
Затем читается искомый файл, при этом каждая строка нумеруется, а команда поиска ищет только те строки номера которых соответствуют номерам из заданного временного файла.
@echo off
:: Вывод n первых строк файла
::
:: Использование:
:: head n filename
::
:: Пример:
:: head 10 %windir%\System32\drivers\etc\hosts
setlocal
:: Необходим временный файл для хранения n номеров первых строк в виде "[n]"
(
for /l %%n in ( 1, 1, %~1 ) do (
echo.[%%n]
)
)>"%TEMP%\$$$head.txt"
:: нумеруем каждую строку
:: ищем только строки с заданным номером и выводим эти строки
:: по окончании временный файл удаляем
for /f "tokens=1,* delims=]" %%n in (
'find /n /v "" "%~2" ^| findstr /b /l /g:"%TEMP%\$$$head.txt" ^&^& del "%TEMP%\$$$head.txt"'
) do (
echo.%%o
)
.
И на последок, традиционно, код на PowerShell:
:: вывести 10 начальных строк файла (2 варианта, первый быстрее)
echo get-content -path %windir%\System32\drivers\etc\hosts -totalcount 10 | powershell -command -
echo "get-content -path %windir%\System32\drivers\etc\hosts | select-object -first 10" | powershell -command -
.
Послесловие
Здесь я привел только заготовки, но уже рабочие заготовки, которые частично решают проблему отсутствия полезных команд. Для полноценной эмуляции можно предусмотреть работу не только с заданным файлом или группой файлов, но и с консолью, и реализовать конвейерную обработку. Также можно было бы добавить проверку установки PowerShell в системе и, если он есть, выполнять его команды.