1 (изменено: Rumata, 2012-08-24 13:50:18)

Тема: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

Предислолвие
Windows не имеет нативных средств для работы с текстовыми файлами из командной строки. Как правильно сказал коллега greg zakharov в теме JScript: чтение N-последних строк файла

greg zakharov пишет:

В 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 в системе и, если он есть, выполнять его команды.

( 2 * b ) || ! ( 2 * b )

2

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

хеад и тайл в одном флаконе

cmd /v:on /q /c "set n=0& for /f "delims=" %x in ('more') do (set /a n=!n!+1> nul& echo.%x& if !n! gtr 9 exit /b)" < Macros.Rus.txt
Я конечно далек от мысли... (с)

3

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

smaharbA, как во флаконе дело обстоит с пустыми строками в файле?

4

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

smaharbA
alexii
Коллеги. Во флаконе только head. Там нет tail.

( 2 * b ) || ! ( 2 * b )

5 (изменено: smaharbA, 2012-08-24 18:04:48)

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

есть там тайл - смотрите внимательно

more +N ...

Я конечно далек от мысли... (с)

6

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

alexii - не знаю, должно быть вроде как нормально, поправьте

Я конечно далек от мысли... (с)

7

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

smaharbA, я имел в виду, что разбор «for /f» пропускает пустые строки.

8

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

понял, учту

Я конечно далек от мысли... (с)

9

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

smaharbA пишет:

more +N

Согласен. Но это не совсем то. В отличие от tail, more +N пропускает N строк из потока. И результат совершенно не предсказуем для потока в конвейере.

( 2 * b ) || ! ( 2 * b )

10 (изменено: Rumata, 2012-08-24 18:19:14)

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

alexii пишет:

разбор «for /f» пропускает пустые строки

Именно поэтому в сообщении входной файл проходит через команду find /n /v "", в этом случае все строки сохраняются.

Но вот посмотрите на этот пример:

( for /l %l in ( 1, 1, 100 ) do @( echo %l & echo. ) ) | cmd /v:on /q /c "set n=0& for /f "delims=" %x in ('more') do (set /a n=!n!+1> nul& echo.%x& if !n! gtr 9 exit /b)"
( 2 * b ) || ! ( 2 * b )

11 (изменено: greg zakharov, 2013-08-25 16:45:07)

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

Rumata пишет:

Код на PowerShell:
echo (get-content -path %windir%...

Слишком сложно, не находите?

#имитация head
gc $env:windir\system32\drivers\etc\hosts | select -first 10
#имитация tail
gc $env:windir\system32\drivers\etc\hosts | selest -last 10

А если нужно передать команду PoSh, то будет примерно так:

powershell /nologo /noprofile /command "& {gc $env:windir\system32\drivers\etc\hosts | select -first 10}"

Скорость же работы PowerShell'а зависит от разных факторов. Бывает и так, что ngen.exe не помогает, в итоге приходится рядом с powershell.exe создовать config со специфичными для каждого случая параметрами.

12

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

Добрый день.
Использовал пример для подсчета количества строк в файле:

more file | find /c /v ""

Отлично работает, но есть минус. Он в итоге считает строчку с символом 'SUB'. Как можно избавиться от подсчета таких строк?

13

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

чем не устраивает ?

find /c /v "" < file.txt
Я конечно далек от мысли... (с)

14

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

2smaharbA
Если есть пустая строка, с символом SUB, её тоже считает.
Проще избавиться от этого символа, что я и сделал.
(При использовании copy, нужно поставить ключ /B)

15

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

smaharbA пишет:

find /c /v "" < file.txt

EQU

find /c /v "" file.txt

???

blackfox.blacktail пишет:

Если есть пустая строка, с символом SUB, её тоже считает.

На оном спотыкается и Ruby (версия старая, до установки последней дело никак не доходит).

16

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

не эквивалентно

Я конечно далек от мысли... (с)

17 (изменено: greg zakharov, 2013-10-13 10:28:42)

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

Rumata пишет:

Возможно, существуют варианты лучше, но, к сожалению, я их не нашел и, поэтому, я использовал "хак" с временным файлом.

head без временных файлов.

smaharbA пишет:

не эквивалентно

Может быть, но сколько бы не пробовал на выходе одинаковый результат. Может у меня на Win2k3 find специфический?

18

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

greg zakharov пишет:
smaharbA пишет:

не эквивалентно

Может быть, но сколько бы не пробовал на выходе одинаковый результат.

Коллега smaharbA прав:

C:\>find /c /v "" c:\autoexec.bat

---------- C:\AUTOEXEC.BAT: 0

C:\>find /c /v "" < c:\autoexec.bat
0

C:\>

19

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

Мальчик-гей пишет:

Коллега smaharbA прав...

Смысл то один и тот же - количество строк в файле. А отбрасывать из вывода имя файла - дело вкуса. Так что это скорее "не равноценно", нежели "не эквивалентно". Или Вы русский язык как-то на свой лад интерпретируете?

20

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

greg zakharov
В данном случае smaharbA прав. Результат обеих команд одинаковый, но вывод - разный. Это как в JavaScript


undefined ==  null // true
undefined === null // false
( 2 * b ) || ! ( 2 * b )

21

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

greg zakharov пишет:

Так что это скорее "не равноценно", нежели "не эквивалентно". Или Вы русский язык как-то на свой лад интерпретируете?

Дорогой коллега! Я понимаю эквивалентность вполне академически, как отношение, обладающее свойствами рефлексивности, симметричности и транзитивности. И в этом смысле на одном и том же классе объектов могут быть заданы различные отношения эквивалентности, так что объекты эквивалентные в одном отношении не будут эквивалентны в другом. Вспомните хотя бы конгруэнтность и подобие геометрических фигур. В рамках же этой темы требовалась более сильная эквивалентность, с совпадением формата вывода результатов, так как всё началось с

Rumata пишет:
more file | find /c /v ""

Команда выведет только размер файла в строках.

чему smaharbA и предложил эквивалентную именно в этом отношении замену.
По поводу "равноценно" и "эквивалентно" советую заглянуть в словарь, а ещё лучше, попробовать самому догадаться как же слово "эквивалентно" переводится на русский.

22 (изменено: greg zakharov, 2013-10-21 16:40:40)

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

Мальчик-гей пишет:

...советую заглянуть в словарь, а ещё лучше, попробовать самому догадаться как же слово "эквивалентно" переводится на русский.

Простите, у Вас филологическое образование? Или Вы страдаете дислексией, что не вполне можете разуметь написанное? Тем паче, что

Мальчик-гей пишет:

Я понимаю эквивалентность вполне академически, как отношение, обладающее свойствами рефлексивности, симметричности и транзитивности.

Вы сами дали понять, что всякую иную интерпретацию слова "эквивалентность" Вы просто-напросто игнорируете, что сужает Ваш кругозор до ортодаксального математического видения проблемы, вкупе с дезинтеграцией русского языка как такового, из чего следует заключить, что спор с Вами представляется трюизмом, а точнее софистикой, - ни одного аргумента с Вашей стороны не последовало.

Rumata пишет:

undefined ==  null // true
undefined === null // false

Не приплетайте сюда, пожалуйста, javascript. null и undefined - это два тривиальных типа данных, каждый из которых определяет только одно значение. Однако, заявлять об их эквивалентности, как-то однако. Считается, что у значение null - объектный тип. При всем этом значение null уникально и отличается от любых других. Если переменная равна null, значит в ней не содержится допустимого объекта (массива, числа, строки и т.д.) undefined же возвращается же либо при обращении к переменной, которй никогда не присваивалось значение, либо к свойству объекта которого не существует. При этом null и undefined не эквивалентны друг другу, несмотря на то, что оператор эквивалентности считает их равными, - это написано в любой книге по javascript. Это можно также утверждать и потому, что в отличии от null значение undefined не является зарезервированным словом. Если будет время, почитайте на досуге, например, Дэвида Флэнегана, если точно помню и не делайте скоропалительных выводов.
Резюмирую, несмотря на общую схожесть многих языков, абсолютизировать понятие эквивалентности не стоит, - если не каждый, то некоторые из языков вкладывают собственное понятие в "эквивалентность", причем будь то естественный язык или язык программирования. То, что выдает в командном языке одинаковый результат, но лишь с некоторым "мусором", знаете ли еще не повод утверждать что заяц - это волк и вообще не млекопитающее, а аргументы г-на Мальчик-гей не убедительны, ибо таким образом можно доказать все что угодно, в том числе что и Ленин был грибом.

23 (изменено: greg zakharov, 2013-10-21 16:22:50)

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

Напоследок:

E:\>find /c /v "" default.htm
---------- DEFAULT.HTM: 137

E:\>find /c /v "" < default.htm
137

Хотите сказать, что 137 не эквивалентно 137-ми? Тогда почему я не в курсе, что отменили элементарную математику?

24

Re: CMD/BAT: Чтение n первых/последних строк в файле; число строк в файле

Перенёс в новую тему: PowerShell: Чтение n последних строк в файле.