Как отфильтровать строку, которая отсчитывается от последней строки из текстового файла с помощью пакетной команды?

Я написал пакетный скрипт для захвата определенной строки местоположения из текстового файла, например:

Текстовый файл содержит:

Copyright Version 5.39
Network activity progressing...


Thread  Time(s) Throughput(KB/s) Avg B / Compl
======  ======= ================ =============
     0  600.088        73245.829     45502.632
     1  594.574        84312.667     44974.835
     2  594.569        62862.184     44547.486
     3  599.665        66407.148     45056.270
     4  600.633        61846.742     44741.495
     5  594.569        45967.918     46745.891
     6  594.937        72678.901     45115.861
     7  593.981        86081.374     45148.288
     8  593.975        35448.661     44118.093
     9  602.451        64144.439     44708.118
    10  599.760        26404.342     53411.916
    11  594.569        64959.044     44525.327
    12  594.564        63125.969     44512.966
    13  602.999        71045.335     45266.114
    14  599.719        19782.849     54192.569
    15  594.574        61670.399     44341.198
    16  599.681        71804.247     44954.492
    17  593.980        21731.533     43903.776
    18  593.979        22436.796     43919.327
    19  599.748        21296.983     53880.446


#####  Totals:  #####


   Bytes(MEG)    realtime(s) Avg Frame Size Throughput(MB/s)
================ =========== ============== ================
   639903.106318     600.002       4289.297         1066.502


Throughput(Buffers/s) Cycles/Byte       Buffers
===================== =========== =============
            17064.032      41.043  10238449.701


DPCs(count/s) Pkts(num/DPC)   Intr(count/s) Pkts(num/intr)
============= ============= =============== ==============
  3081479.450         0.085     2302796.649          0.113


Packets Sent Packets Received Retransmits Errors Avg. CPU %
============ ================ =========== ====== ==========
    66199455        156432871           4      0     99.998

Всего в текстовом файле 58 строк, и мне нужно будет получить значение Throughput(MB/s), которое выделено в строке 43, и 4-ю строку, из приведенного выше примера, это 1066.502.

Итак, я создал пакетный скрипт для фильтрации числа:

set "lom1 = "
for /f "skip=48 delims = " %%i in LOM_1.log) do if not defined lom1 set "lom1=%%i"
echo !lom1! >> temp1.txt
for /f "tokens=4" %%j in temp1.txt do echo %%j >> result.log

Но теперь исходный текст содержит ошибки и увеличивает случайные строки, например:

Copyright Version 5.39
Network activity progressing...
ERROR: WaitForWorkerThreads failed: WaitForMultipleObjects returned an unexpected value

ERROR: DoWork failed: WaitForWorkerThreads(threads_finished) timed out
ERROR: StartSenderReceiver in thread: 9 failed: closesocket, GetLastError: 10093 - Either the application has not called WSAStartup, or WSAStartup failed.

ERROR: StartSenderReceiver in thread: 13 failed: closesocket, GetLastError: 10093 - Either the application has not called WSAStartup, or WSAStartup failed.

ERROR: StartSenderReceiver in thread: 16 failed: closesocket, GetLastError: 10093 - Either the application has not called WSAStartup, or WSAStartup failed.



Thread  Time(s) Throughput(KB/s) Avg B / Compl
======  ======= ================ =============
     0  600.088        73245.829     45502.632
     1  594.574        84312.667     44974.835
     2  594.569        62862.184     44547.486
     3  599.665        66407.148     45056.270
     4  600.633        61846.742     44741.495
     5  594.569        45967.918     46745.891
     6  594.937        72678.901     45115.861
     7  593.981        86081.374     45148.288
     8  593.975        35448.661     44118.093
     9  602.451        64144.439     44708.118
    10  599.760        26404.342     53411.916
    11  594.569        64959.044     44525.327
    12  594.564        63125.969     44512.966
    13  602.999        71045.335     45266.114
    14  599.719        19782.849     54192.569
    15  594.574        61670.399     44341.198
    16  599.681        71804.247     44954.492
    17  593.980        21731.533     43903.776
    18  593.979        22436.796     43919.327
    19  599.748        21296.983     53880.446


#####  Totals:  #####


   Bytes(MEG)    realtime(s) Avg Frame Size Throughput(MB/s)
================ =========== ============== ================
   639903.106318     600.002       4289.297         1066.502


Throughput(Buffers/s) Cycles/Byte       Buffers
===================== =========== =============
            17064.032      41.043  10238449.701


DPCs(count/s) Pkts(num/DPC)   Intr(count/s) Pkts(num/intr)
============= ============= =============== ==============
  3081479.450         0.085     2302796.649          0.113


Packets Sent Packets Received Retransmits Errors Avg. CPU %
============ ================ =========== ====== ==========
    66199455        156432871           4      0     99.998

Вы можете видеть, что в текстовом файле есть дополнительные строки ERROR, это означает, что я все еще не могу использовать команду for /f "skip=48" для фильтрации правильной строки, поэтому я хотел бы знать, можно ли использовать какую-либо пакетную команду или параметры for /f для считать от последней строки, чтобы всегда был фиксированный номер строки, например, в текстовом содержимом выше. Если я считаю от последней строки и мне нужна строка 16th, то я могу продолжить экспорт строки 16th ( считать с последней строки), а затем захватить строку 4th для числа.

Я пытался гуглить, но не нашел полезной информации.

Я бы подошел к этому, просто используя for /f`` loop on the output of a findstr`, который игнорирует строки, содержащие =, Используя логическое значение, чтобы определить, является ли четвертый токен строки Throughput(MB/s), и когда это логическое значение set, grab the fourth token of the next line, exit the for` и отобразить.

Magoo 15.03.2024 09:20

@Magoo звучит хорошо, но извините, не могли бы вы дать мне пример кода, чтобы я мог изменить его в соответствии с тем, что мне нужно, извините, я здесь новичок...

Jacky Lee 15.03.2024 10:15

Если вам нужна только цифра Throughput(MB/s) из утилиты командной строки ntttcp.exe, нужен ли вам вообще текстовый файл?

Compo 15.03.2024 10:26

@Compo Да, мне все еще нужен текстовый файл в качестве журнала тестирования, скажем, он будет сгенерирован сценарием стресс-теста, будет выполняться не только один раз, но и в течение ночи, по 5 минут каждый цикл, поэтому будет много файлов журналов и их необходимо сохранить, для тестового сценария мне нужно собрать все показатели производительности в один файл для быстрого просмотра (поэтому мне не нужно открывать каждый файл журнала, чтобы проверить номер), но исходный журнал все равно необходимо сохранить для дальнейшей отладки.

Jacky Lee 15.03.2024 10:33

Итак, вы хотите сделать это для нескольких файлов, как они называются? должен быть шаблон именования файлов, если это делается в запланированном цикле. Гораздо больше смысла в решении, которое читает их все, а не только одно.

Compo 15.03.2024 10:58

На DosTips.com есть замечательная функция, которую можно использовать для извлечения разделов файла. dostips.com/DtCodeCmdLib.php#Function.extractFileSection

Squashman 15.03.2024 15:59

В приведенном выше файле примера всего 49 строк, а не 58, и Throughput(MB/s) находится в строке 32 вместо 43. Итак, то, что вы сказали (всего 58 строк...) неверно.

phuclv 16.03.2024 17:42

@Compo Предположим, что тестовый сценарий будет выполнять тест в течение 5 минут, а затем перезагрузит систему, после загрузки ОС снова запустит 5-минутный стресс-тест и зациклится на ночь. Мой тестовый сценарий может переименовывать журнал испытаний и сохранять его для каждого цикла тестирования, так что это не является проблемой для нескольких файлов журнала испытаний, и мой тестовый сценарий будет делать то же самое в журнале испытаний, когда проводится 5-минутный стресс-тест. завершено, и создается журнал испытаний.

Jacky Lee 17.03.2024 05:24

@Compo И это то, что я сказал выше: записать показатель производительности в другой текстовый файл для сбора всех показателей производительности с помощью циклических тестов, что позволяет мне легко быстро просмотреть все показатели производительности после завершения ночного стресс-теста.

Jacky Lee 17.03.2024 05:25

@phuclv не совсем, пустую строку также необходимо учитывать, поскольку для командного файла она даже пуста, но все равно остается строкой. Содержимое примера, которое я публикую, представляет собой всего лишь 100% копирование и вставку сюда, включая пустые строки.

Jacky Lee 17.03.2024 05:26

@JackyLee, конечно, пустые строки учитываются. Я скопировал то, что вы написали выше, вставил в файл и пользуюсь wc -l

phuclv 20.03.2024 08:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
11
148
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

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

@Echo Off
SetLocal EnableExtensions DisableDelayedExpansion

PushD "P:\ath To\Log Directory" 2>NUL || Exit /B

(   For /F "Tokens=1-2 Delims=:" %%G In ('%SystemRoot%\System32\findstr.exe
     /INR "Throughput(MB/s)" "lom_*.log" 2^>NUL') Do Call :Sub "%%G" "%%H"
) 1>"%~dp0result.log"
Pause

PopD
EndLocal
GoTo :EOF

:Sub
Set /A "_l=%~2 + 1"
For /F "Tokens=4" %%I In ('%SystemRoot%\System32\more.com +%_l% "%~1"'
) Do Echo %%I& GoTo :EOF

Очевидно, вам следует изменить путь к файлу журнала в четвертой строке, и обратите внимание, что я предположил, что имена файлов журнала имеют шаблон lom_*.log. Полученный файл будет находиться в том же каталоге, что и ваш командный файл.

Обратите внимание: если вы обрабатываете только один файл за один запуск пакетного файла, а не целый каталог, полный их, вам нужно будет изменить только 1> на 1>> в восьмой строке.

Я несколько неправильно прочитал ваши данные, отметив, что теперь требуется Throughput(MB/s) токен 6, а необходимые данные находятся в токене 4.

Не уверен, хотите ли вы обрабатывать несколько файлов — если нет, внешний цикл %%y не нужен и замените имя файла на %%y в оставшемся цикле.

%%b получит токен 4 и %%c токен 6 в обработанных строках

@ECHO OFF
SETLOCAL
rem The following setting for the directory and filenames are names
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.

SET "sourcedir=u:\your files"
SET "destdir=u:\your results"
SET "outfile=%destdir%\outfile.txt"

(
FOR %%y IN ("%sourcedir%\q78165354*.txt") DO (
 SET "required = "
 SET "grabnext = "
 FOR /f "tokens=4,6delims= " %%b IN ('findstr /v /L /c:" = " "%%y"') DO IF /i "%%c"= = "Throughput(MB/s)" (SET "grabnext=y") ELSE (
  IF NOT DEFINED required IF DEFINED grabnext SET required=%%b&ECHO %%b %%y
 )
)
)>"%outfile%"
GOTO :EOF

rem Всегда проверяйте тестовый каталог перед применением к реальным данным.

Для тестирования я использовал файлы с именем q78165354*.txt, содержащие ваши данные.

Ответ принят как подходящий

Здесь другой и более простой подход. Вместо того, чтобы получить строку на две строки ниже Throughput(MB/s), мы можем получить строку на три строки выше Throughput(Buffers/s), которую можно получить с помощью одной команды findstr.

@echo off
setlocal EnableDelayedExpansion

rem Get a CR+LF (NewLine) variable
for /F %%a in ('copy /Z "%~F0" NUL') do set ^"NL=%%a^
%Don't remove this line%
^"

rem Get the appropriate line and get its 4th token
findstr "!NL!!NL!!NL!Throughput(Buffers/s)" LOM_1.log | for /F "tokens=4" %%a in ('more') do echo %%a

Если вы хотите обработать несколько файлов *.log, просто вставьте команды findstr ... | for ... в цикл обработки файлов.

РЕДАКТИРОВАТЬ 21.03.2024: Получить номер ошибки.

Я читал ваш запрос в комментариях ниже ответ phuclv о получении номера «Ошибки». Получить такое число очень просто в батнике:

Если Packets Sent Packets Received Retransmits Errors Avg. CPU % — последний раздел в файле, просто сделайте следующее:

for /F "tokens=4" %%a in (LOM_1.log) do set "Errors=%%a"

и это все! ;)

@Compo: Боюсь, ты прочитал не мой ответ...

Aacini 16.03.2024 00:42

Заметил это, Аацини, извини.

Compo 16.03.2024 00:44

В PowerShell это намного проще: если содержимое не исправлено, вы можете использовать это, чтобы сопоставить строку 2nd после Throughput(MB/s) и получить последнее слово:

(sls -Co 0,2 'Throughput(MB/s)' -S file.txt).Context.PostContext.Split(' ')[-1]

Если вы хотите получить именно последний токен в строке 43, используйте это:

(gc file.txt | select -Skip 42 -F 1).Split(' ')[-1]

(some command).Split(' ') разделит результат some command по пробелу ' ' на массив, а затем [-1] получит последний элемент массива

Полное объяснение и версии без псевдонимов ниже.


В любом случае ваш вопрос не соответствует названию. Если вы хотите, чтобы строка Nth считалась снизу, что похоже на tail -<N> file.txt | head -1 в *nix, то это совершенно другое. Чтобы получить 17-ю строку из последней строки, просто используйте это

 gc file.txt -Ta 17 | select -F 1

Вы можете вызвать его из cmd, если вы действительно не можете избежать cmd следующим образом:

powershell -C "Get-Content file.txt -Tail 17 | Select-Object -First 1"
powershell -Command "(gc file.txt | select -Skip 42 -F 1).Split(' ')[-1]"

Но просто избегайте cmd в новом коде, в PowerShell все проще.


Вот полная версия приведенных выше команд без псевдонимов.

# Get the 43th line by skipping the first 42, then
# split the string by space ' ' and get the last word
(Get-Content file.txt | Select-Object -Skip 42 -First 1).Split(' ')[-1]

# Same as above but counting up skipping the last 16 lines instead
(Get-Content file.txt -Tail 17 | Select-Object -First 1).Split(' ')[-1]

# Find the line containing 'Throughput(MB/s)' and 2 lines of context after that,
# matching the string literally instead of regex, then extract the last word
# on the 2nd context line
(Select-String -Context 0,2 'Throughput(MB/s)' `
    -SimpleMatch file.txt).Context.PostContext.Split(' ')[-1]

дополнительный вопрос: если я хочу получить номер «Ошибки», каким должно быть последнее значение в следующей команде: powershell (Select-String -Context 0,2 'Errors' ` -SimpleMatch LOM_1.log).Context.PostContext.Split (' ')[ценить]

Jacky Lee 18.03.2024 07:22

@JackyLee это просто обычная индексация массива, как в Python или некоторых других языках сценариев. Для массива a тогда a[i] — это i-й элемент с начала, если i >= 0, и с конца, если i < =, поэтому i = -1 берет последний элемент.

phuclv 20.03.2024 08:03

phuclv Да, но попробовал -2, чтобы захватить два последних элемента, но не удалось. Если я использую -1, то я получу число «Средний процент ЦП» (последний элемент, как вы сказали), но не «Ошибки» из моего примера журнала в первом сообщении, поэтому я просто попробовал -2, чтобы получить количество «Ошибок», но безуспешно.

Jacky Lee 21.03.2024 03:26

@JackyLee, это потому, что обычная функция String.Split(), используемая в этом случае, просто допускает использование одного символа-разделителя в наборе. Вам понадобится оператор -split, если вы хотите разделить, используя несколько последовательных символов-разделителей: PS /Users/phluu> ((Select-String -Context 0,2 "Avg. CPU %" -SimpleMatch file.txt).Context.PostContext -split '\s+')[-4..-1]. Это напечатает последние 4 элемента

phuclv 21.03.2024 12:47

@JackyLee В качестве альтернативы вы можете использовать перегрузку, позволяющую обрезать: ((Select-String -Context 0,2 "Avg. CPU %" -S file.txt).Context.PostContext.Split(' ', [StringSplitOptions]::TrimEntries + [StringSplitOptions]::RemoveEmptyEntries))[-5..-2]: будут напечатаны последние элементы со 2 по 5. Более короткий метод: (-split (sls -Co 0,2 "Avg. CPU %" -S file.txt).Context.PostContext)[-4..-1]

phuclv 21.03.2024 12:51

Вы можете прочитать файл, использовать find, чтобы определить номер строки Througput(MB/s), затем добавить к нему единицу, чтобы получить номер строки, результатом которой является ваш 4-й токен:

@echo off & set "tput = "
for /F "delims=[]" %%i in ('find /N "Throughput(MB/s)" "LOM_1.log"') do set /a ln=1+%%i
for /f "usebackq skip=%ln%tokens=4delims= " %%i in ("LOM_1.log") do if not defined tput set "tput=%%i"
echo %tput%

Добавьте >> result.log в последнюю строку, если вам нужно сохранить его в файле.

Другие вопросы по теме