Зациклить поиск и заменить строку, состоящую из двух частей, поверх файла с помощью PowerShell, сохранив при этом одну из частей

Я новичок в PowerShell и не нашел вопроса о переполнении стека или ссылки на документацию, которая позволила бы мне полностью привести меня к успешному результату. Если уже существует вопрос или ссылка на документацию, которая отвечает на этот вопрос, который я пропустил, я был бы признателен за информацию.

В текстовом файле есть такая строка:

<span><span><span><span><span></span></span></span></span></span>

Количество <span> и </span> варьируется от файла к файлу. Например, в некоторых файлах это так:

<span></span>

А у других это так:

<span><span></span></span>

И так далее. Скорее всего, в строке никогда не будет больше 24 штук каждого типа.

Я хочу удалить все подобные строки в текстовом файле, но сохранить </span> в таких строках:

<span style = "font-weight:bold;">text</span>

В текстовом файле может быть много вариантов такой строки; например, <span style = "font-size: 10px; font-weight: 400;">text</span> или <span style = "font-size: 10px; font-weight: 400;">text</span>, и я заранее не знаю, какие варианты будут включены в текстовый файл.

Это частично работает ...

$original_file = 'in.txt'
$destination_file = 'out.txt'

(Get-Content $original_file) | Foreach-Object {
    $_ -replace '<span>', '' `
       -replace '</span>', ''
} | Set-Content $destination_file

... но, очевидно, приводит к чему-то вроде <span style = "font-weight:bold;">text.

В приведенном выше сценарии PowerShell я могу использовать

    $_ -replace '<span></span>', '' `

Но, конечно, он ловит <span></span> только в середине строки, потому что, как сейчас написано, он не зацикливается.

Я знаю, что глупо делать что-то подобное

$original_file = 'in.txt'
$destination_file = 'out.txt'

(Get-Content $original_file) | Foreach-Object {
    $_ -replace '<span></span>', '' `
       -replace '<span></span>', '' `
       -replace '<span></span>', '' `
       -replace '<span></span>', '' `
       -replace '<span></span>', '' 
} | Set-Content $destination_file

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

Я считаю, что необходимо добавить логику в следующие строки:

   foreach($i in 1..24){
    Write-Host $i

Но так и не удалось успешно включить это в сценарий.

Если это совершенно неправильный подход, я был бы признателен за информацию.

Причина использования PowerShell в том, что моя команда предпочитает его для скриптов, включенных в конвейер выпуска Azure DevOps.

Спасибо за любые идеи или помощь.

🤔 А знаете ли вы, что...
PowerShell предоставляет инструменты для мониторинга производительности системы и приложений.


2
1 697
5

Ответы:

Вы можете использовать регулярное выражение вместе с оператором -replace, чтобы удалить все пары <span>optional content</span> из строки. Это означает все пары, в которых открывающий тег не определяет никаких атрибутов.

$content = '<span></span><span><span><span style = "font-weight:bold;">Foo</span></span></span>'
$regex = '<span>(.*?)</span>'    
while ($content -match $regex)
{
    $content = $content -replace $regex,'$1'
}
Write-Output $content

В результате получится:

<span style = "font-weight:bold;">Foo</span>

Цикл while заботится о ваших вложенных вхождениях пары <span></span>.


Попробуйте следующее ... Я добавил несколько комментариев, чтобы прояснить ситуацию.

# always use absolute paths if possible
$original_file = 'c:\tmp\in.txt'
$destination_file = 'c:\tmp\out.txt'

$patternToBeRemoved = '<span></span>'

# store the file contents in a variable
$fileContent = Get-Content -Path $original_file

# save the result of these operations in a new variable and iterate through each line
$newContent = foreach($string in $fileContent) {
    # while the pattern you don't want is found it will be removed
    while($string.Contains($patternToBeRemoved)) {
        $string = $string.Replace($patternToBeRemoved, '')
    }
    # when it's no longer found the new string is returned
    $string
}

# save the new content in the destination file
Set-Content -Path $destination_file -Value $newContent

$original_file = 'in.txt'
$destination_file = 'out.txt'

ForEach ($Line in (Get-Content $original_file)) {
    Do {
        $Line = $Line -replace '<span></span>',''
    } While ($Line -match '<span></span>')
    Set-Content -Path $destination_file -Value $Line 
}

$content = '<span></span><span><span><span style = "font-weight:bold;">Foo</span></span></span>'
$regex = '<span\s+[^<]+</span>'
$null = $content -match $regex

$Matches[0]

Решено

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

$original_file = 'in.txt'
$destination_file = 'out.txt'

(Get-Content $original_file) -replace "(<span>)+(</span>)+" | 
 Set-Content $destination_file