Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Скриптовые языки администрирования Windows (http://forum.oszone.net/forumdisplay.php?f=102)
-   -   [решено] Удаления строки ниже найденого текста. (http://forum.oszone.net/showthread.php?t=341820)

Grade24 15-08-2019 10:45 2884112

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

Есть текстовый файл вида.

произвольный текст
произвольный текст
произвольный текст txt1 произвольный текст
произвольный текст
произвольный текст
произвольный текст txt1 произвольный текст
произвольный текст
произвольный текст txt1 произвольный текст
произвольный текст
произвольный текст
произвольный текст

Где:
произвольный текст - это различный текст, не повторяющийся текст.
text1 - это текст который не изменяется.

Задача, найти в текстовом файле текст "txt1" удалить строчку где находиться этот текст и строчку ниже.

Iska 15-08-2019 11:08 2884120

Цитата:

Цитата Grade24
Есть текстовый файл вида. »

Упакуйте его в архив и приложите к сообщению.

Цитата:

Цитата Grade24
text1 - это текст который не изменяется. »

Приведите конкретный пример содержимого text1 на основании файла, упакованного в архив.

Вас устроит рещение на WSH/PowerShell?

Grade24 15-08-2019 13:01 2884158

Вложений: 1
Спасибо за быстрый ответ. Увы в файле есть информация которую не могу выложить. Но думаю Вы поймете вчем суть для примера прилагаю файл.

В файле 12 строк, в файле есть три строки где присутствует текст txt1 в строчках 5,7 и 11. Необходимо удалить эти строчки (5,7,11) и удалить строчки после них, строчки 6,8,12.

Рабочий файл содержить почти 4000 строк. Текст по которому ищем строчки (text1) может расспологаться в различных строчках.

Цитата:

Цитата Iska
Вас устроит рещение на WSH/PowerShell? »

Да, вполне.

YuS_2 15-08-2019 13:58 2884172

Цитата:

Цитата Grade24
Текст по которому ищем строчки (text1) может расспологаться в различных строчках. »

А если строки будут расположены так:
Код:

1. произвольный текст
2. произвольный текст txt1 произвольный текст
3. произвольный текст txt1 произвольный текст
4. произвольный текст

По Вашему условию: удаляется строка №2, т.к. в ней содержится маркер "txt1" и за ней сразу же удаляется строка №3, т.к. это последующая строка за строкой с маркером.
Проверить маркер в строке №3 мы уже не можем, т.к. строка удалена...
В сухом остатке: строку №4 удалять уже нельзя, т.к. не выполняются необходимые условия.
А как должно быть?

Grade24 15-08-2019 14:03 2884173

Цитата:

Цитата YuS_2
По Вашему условию: удаляется строка №2, т.к. в ней содержится маркер "txt1" и за ней сразу же удаляется строка №3, т.к. это последующая строка за строкой с маркером.
Проверить маркер в строке №3 мы уже не можем, т.к. строка удалена...
В сухом остатке: строку №4 удалять уже нельзя, т.к. не выполняются необходимые условия.
А как должно быть? »

Все нормально, такого не будет, после строчки с txt1 всегда идет строчка без txt1

Iska 15-08-2019 14:37 2884182

Цитата:

Цитата YuS_2
А если строки будут расположены так: »

А я даже не подумал о таком варианте :(.

Цитата:

Цитата Grade24
in866.rar »

На всякий случай спрошу. Файл у вас именуется in866.txt, но кодировку имеет UTF-8 с BOM. Так и должно быть? И последняя строка в Вашем образце не завершается символами конца строки — это тоже верно, так и есть?

YuS_2 15-08-2019 15:01 2884190

Цитата:

Цитата Grade24
Все нормально, такого не будет, после строчки с txt1 всегда идет строчка без txt1 »

Ну, если есть уверенность, это хорошо, но т.к. нет полных исходных данных, то лучше учесть всякие варианты...
Примерно так:
powershell
Код:

$file = '.\in866.txt'
$mrk = 'txt1'
$enc = 'utf8'

$a = gc $file -enc $enc|%{$flg1,$flg2=$false,$false}{
        if($_ -match $mrk){
                $flg1=$true
                $flg2=$true
        } elseif ($flg1 -and $flg2){
                $flg2=$false
        } else {
                $_
                $flg1=$false
                $flg2=$true
        }
}
$a|sc $file -enc $enc

в том числе, будет работать, если есть парные строки с маркером.

Цитата:

Цитата Iska
А я даже не подумал о таком варианте »

Дык, всего на свете учесть невозможно.
Просто приходилось сталкиваться с подобной постановкой задачи, когда прилегающие, контекстные строки надо было учитывать... кстати, тот код ваял ещё на cmd и если хорошо поискать, то возможно даже он найдется... :)

Grade24 15-08-2019 15:20 2884192

YuS_2,
Спасибо за код, увы не работает. Может что-то нетак делаю. Создал файл 123.bat, файл находиться в тойже папке что и файл in866.txt, запускай 123.bat, но нечего не меняется... Что я делаю нетак ? Пробовал в первой строчке прописать полный путь до файла in866.txt, не помогло.

Цитата:

Цитата YuS_2
Ну, если есть уверенность, это хорошо, но т.к. нет полных исходных данных, то лучше учесть всякие варианты... »

Я просто не стал углубляться в структуру исходного файла, не хотел вам забивать голову. Если пригодиться расскажу )) Постараюсь максимально понятно объяснить...

Файл состоит из блоков строк, один блок - это две строки, между блоками нет пустых строк. У всех блоков произвольный, разный, не повторяющийся текст. Но есть блоки где первая строчка содержит текст txt1 эти блоки необходимо удалить, тоесть удалить первую и вторую строчку блока.

Iska 15-08-2019 15:45 2884204

Цитата:

Цитата Grade24
Создал файл 123.bat, »

Это не пакетный файл, это скрипт PowerShell.

А на мои вопросы:
Цитата:

Цитата Iska
На всякий случай спрошу. Файл у вас именуется in866.txt, но кодировку имеет UTF-8 с BOM. Так и должно быть? И последняя строка в Вашем образце не завершается символами конца строки — это тоже верно, так и есть? »

ответите?

YuS_2 15-08-2019 17:21 2884234

Цитата:

Цитата Grade24
Создал файл 123.bat, файл находиться в тойже папке что и файл in866.txt, запускай 123.bat, но нечего не меняется... Что я делаю нетак ?»

Если по-простому, то всё не так... :)
То, что это не batch-сценарий, уже написали...
В общем, скрипт необходимо сохранить в текстовом файле, с кодировкой UTF8 с BOM (это не единственная кодировка, но лучше в ней). Файл можно именовать как угодно, но расширение должно быть .ps1
Например: script.ps1
Далее, есть варианты...
Скрипт можно запускать непосредственно из консоли powershell, либо ПКМ на файле скрипта и выбрать соответствующий пункт контекстного меню, либо создать batch-сценарий, например, run.bat:
Код:

@echo off
echo Выполняем...
powershell -ex bypass .\script.ps1
echo Готово.
pause

и запускать сценарий powershell уже этим пакетным файлом...
В общем, выбор за Вами...

greg zakharov 15-08-2019 21:19 2884263

Проще всего задача решается с помощью sed:
Код:

sed "/txt1/,+1 d" input.txt
В итоге из:
Код:

1произвольный текст
2произвольный текст
3произвольный текст txt1 произвольный текст
4произвольный текст
5произвольный текст
6произвольный текст txt1 произвольный текст
7произвольный текст
8произвольный текст txt1 произвольный текст
9произвольный текст
10произвольный текст
11произвольный текст

Получим:
Код:

1произвольный текст
2произвольный текст
5произвольный текст
10произвольный текст
11произвольный текст


Iska 16-08-2019 14:13 2884345

Добавлю и своё до кучи (на PowerShell):
Скрытый текст
Код:

$sSourceFile  = 'C:\Мои проекты\0272\in866 2.txt'
$sString4Find = 'txt1'


$sPattern = ".*$sString4Find.*`r`n.*(?:`r`n|$)"

if([System.IO.File]::Exists($sSourceFile)) {
    $sContent = Get-Content -Path $sSourceFile -Encoding UTF8 -Raw

    if([Regex]::IsMatch($sContent, $sPattern)) {
       
        [Regex]::Replace($sContent, $sPattern, '') | Set-Content -Path $sSourceFile -Encoding UTF8 -NoNewline
        Write-Host "Found [$sString4Find] and replaced in content of source file [$sSourceFile]." -ForegroundColor Green
    } else {
        Write-Host "Not found [$sString4Find] in content of source file [$sSourceFile]." -ForegroundColor Red
    }
} else {
    Write-Host "Can't find source file [$sSourceFile]." -ForegroundColor Red
}


Grade24 16-08-2019 14:17 2884346

Друзья большое всем спасибо за помощь, то что нужно.

Использую этот код, без паузы в конце.
Код:

@echo off
echo Выполняем...
powershell -ex bypass .\script.ps1
echo Готово.
pause

Для запуска этого.
Код:

$file = '.\in866.txt'
$mrk = 'txt1'
$enc = 'utf8'

$a = gc $file -enc $enc|%{$flg1,$flg2=$false,$false}{
        if($_ -match $mrk){
                $flg1=$true
                $flg2=$true
        } elseif ($flg1 -and $flg2){
                $flg2=$false
        } else {
                $_
                $flg1=$false
                $flg2=$true
        }
}
$a|sc $file -enc $enc

Ребята, еще раз, от души спасибо. Нужно сделать на форуме кнопки доната для пользователей. Готов задонатить.

Кстати, если искать текст (маркер) вида 123+, то удаляет больше строк чем нужно. Возможно из-за "+", для меня это не критично.

Попробую код от Iska

Iska 16-08-2019 15:43 2884363

Цитата:

Цитата Grade24
Кстати, если искать текст (маркер) вида 123+, то удаляет больше строк чем нужно. Возможно из-за "+", для меня это не критично. »

Всё так. «+» в шаблоне регулярного выражения имеет специальный смысл — это квалификатор, означающий «предыдущий символ шаблона должен встретиться один или более раз». Чтобы использовать сам символ плюс, нужно его экранировать другим специальным символом:
Код:

123\+
Вот здесь: Элементы языка регулярных выражений — краткий справочник | Microsoft Docs Вы можете загрузить (Загрузить в формате Word (DOCX) Download in Word (.docx) format) краткую справку по символам регулярных выражений .Net Framework.

AKadekin 19-08-2019 09:26 2884689

Добрый день, как в PowerShell в скрипте ps сохранить результат не в utf8 c BOM а в формате utf8 без BOM

Iska 19-08-2019 15:24 2884735

Примерно так.

DJ Mogarych 23-08-2019 13:52 2885199

YuS_2, а разъясните, пожалуйста, как вот это работает:
Код:

% {$flg1,$flg2=$false,$false}{...}
Почему после % идёт два скриптблока, и что задаётся в первом?

Iska 23-08-2019 14:20 2885201

DJ Mogarych, насколько я понимаю, % — псевдоним командлета ForEach-Object, который поддерживает три скрипт-блока — Begin, Process и End. В данном случае использованы скрипт-блоки Begin и Process. Соответственно, в первом скрипт-блоке Begin двум переменным просто присваиваются два булевых значения, запись вида:
Код:

$flg1,$flg2=$false,$false
аналогична более привычной записи:
Код:

$flg1=$false
$flg2=$false

Данная конструкция, именуемая «параллельное присваивание», вида «список переменных=список выражений», присутствует во многих современных языках — Lua, Python и пр.

greg zakharov 23-08-2019 15:11 2885205

В случае с PowerShell можно было бы обойтись "многострочной" регуляркой, в смысле захватываем строку соответствующую критерию и следующую строку за ней. Это будет работать за рядом оговорок, но можно сделать несколько иначе.
Код:

$indexes = ($content = Get-Content .\file.txt -Encoding utf8) |
Select-String txt1 | ForEach-Object {++($$ = ,(--$_.LineNumber) * 2)[1];$$}

0..($content.Length - 1) | Where-Object {$indexes -notcontains $_} |
ForEach-Object {$content[$_]} | Out-File .\cleaned.txt -Encoding utf8

В более поздних нежели v2 версиях PowerShell, данный подход можно еще более сократить за счет оператора -notin, методов расширений и т.д., в результате чего решение будет похоже скорее на black magic, примерно такое же как в случае с "многострочной" регуляркой.

YuS_2 23-08-2019 17:42 2885214

Цитата:

Цитата DJ Mogarych
разъясните, пожалуйста, как вот это работает »

Да, Iska, верно описал работу конструкции и добавить особо нечего...
А если, что-нибудь забудется, то можно всегда освежить в памяти, с подробностями и примерами:
Код:

man foreach-object -s

DJ Mogarych 23-08-2019 21:44 2885242

Я пытался "захватить следующую строку" с помощью Regex типа
Код:

".*txt1.*\n.*"
или
Код:

".*txt1.*\r.*"
, но ничего не вышло.
Также, сначала убивал строку с паттерном, а потом пытался захватить пустую строку и следующую за ней:
Код:

"\s+\n.*"
, но тоже не сработало.

Iska 23-08-2019 22:05 2885247

Цитата:

Цитата DJ Mogarych
, но ничего не вышло. »

То есть?

Скрытый текст
Код:

$sContent = @'
1. произвольный текст
2. произвольный текст
3. произвольный текст txt1 произвольный текст
4. произвольный текст
5. произвольный текст
6. произвольный текст txt1 произвольный текст
7. произвольный текст
8. произвольный текст txt1 произвольный текст
9. произвольный текст
10. произвольный текст
11. произвольный текст
'@

[RegEx]::IsMatch($sContent, ".*txt1.*\n.*\n")
[RegEx]::Replace($sContent, ".*txt1.*\n.*\n", "")

Цитата:

Код:

True
1. произвольный текст
2. произвольный текст
5. произвольный текст
10. произвольный текст
11. произвольный текст



Шаблон будет нуждаться в доработке для охвата граничных случаев, но так-то — работает, не?

YuS_2 24-08-2019 07:51 2885263

Цитата:

Цитата DJ Mogarych
но ничего не вышло. »

Цитата:

Цитата DJ Mogarych
но тоже не сработало. »

Тут важно, как подается текст на обработку регэкспами (построчно или массивом)... скорее всего, в этом проблема.

DJ Mogarych 24-08-2019 08:52 2885265

Вот так не работает, хотя почему - непонятно.
Код:

(gc C:\temp\123.txt) -notmatch ".*txt1.*\n.*\n"
В данном случае то, что в скобках - это массив.
Код:

(gc C:\temp\123.txt).GetType()

IsPublic IsSerial Name                                    BaseType                                                                                         
-------- -------- ----                                    --------                                                                                         
True    True    Object[]                                System.Array


Iska 24-08-2019 09:22 2885268

DJ Mogarych, так — да, не будет работать, потому что командлет Get-Content возвращает именно массив отдельных строк (в которых нет символа \n), а не одну строку с символами «конец строки» внутри. Нужно добавить в командлет Get-Content параметр -Raw, тогда должно получиться то, что нужно.

YuS_2 24-08-2019 09:28 2885269

Цитата:

Цитата DJ Mogarych
(gc C:\temp\123.txt) -notmatch ".*txt1.*\n.*\n" »

а так:
Код:

(gc C:\temp\123.txt -raw) -notmatch ".*txt1.*\n.*\n"
?
По сути, Ваша запись соответствует:
Код:

gc C:\temp\123.txt|%{$_ -notmatch ".*txt1.*\n.*\n"}
А чтобы было наглядно, какие процессы происходят:
Код:

gc C:\temp\123.txt|%{$_;'==='}
gc C:\temp\123.txt -raw|%{$_;'==='}

- в консоли будет видна разница...

DJ Mogarych 24-08-2019 09:46 2885270

Спасибо!
Вот так работает:
Код:

(gc C:\temp\123.txt -raw) -replace ".*txt1.*\n.*\n"
Да, я догадался уже после того, как написал последнее сообщение, что массив рассматривается не как единый объект, а каждая строка рассматривается по отдельности.

YuS_2 24-08-2019 12:55 2885286

Цитата:

Цитата DJ Mogarych
а каждая строка рассматривается по отдельности »

Да, тут главное - не запутаться в терминологии... массив текста, по сути, один объект, а массив строк - множество объектов, по одному на каждую строку...


Время: 15:08.

Время: 15:08.
© OSzone.net 2001-