Могу ли я разбить этот файл на разделы по строкам, избегая поведения многострочного массива powershell? - PullRequest
1 голос
/ 23 октября 2019

У меня есть текстовая распечатка DISA STIG. Это выглядит так:

Group ID (Vulid):  V-96855
Group Title:  SRG-NET-000018-RTR-000001
Rule ID:  SV-105993r1_rule
Severity: CAT II
 Rule Version (STIG-ID):  CISC-RT-000010
Rule Title: The Cisco router must be configured to enforce approved authorizations for controlling the flow of information within the network based on organization-defined information flow control policies.
...
_____________________________________________________________

 Group ID (Vulid):  V-96857
Group Title:  SRG-NET-000025-RTR-000020
Rule ID:  SV-105995r1_rule
Severity: CAT II
 Rule Version (STIG-ID):  CISC-RT-000020
Rule Title: The Cisco router must be configured to implement message authentication for all control plane protocols.
...
_____________________________________________________________

 Group ID (Vulid):  V-96859
Group Title:  SRG-NET-000025-RTR-000085
Rule ID:  SV-105997r1_rule
Severity: CAT II
 Rule Version (STIG-ID):  CISC-RT-000030
Rule Title: The Cisco router must be configured to use keys with a duration not exceeding 180 days for authenticating routing protocol messages.
...

Я бы хотел обработать это, но у меня возникают проблемы с тем, как powershell обрабатывает многострочные вводы. Я пытаюсь разделить каждое правило (между ____ делителями) в отдельный раздел, а затем превратить его в xml. Когда я импортирую контент и пытаюсь разделить его, он либо выходит по одной строке за раз, что означает, что я не могу очень хорошо обработать разделитель, или я пытался закодировать его как строку, и в этом случае каждыйперсонаж выходит одновременно.

    $rawContent = Get-Content C:\Users\ncfx\Projects\SignatureRWK\raw.txt -Raw

    $splitRules = $rawContent.Split("_____________________________________________________________")

    Foreach ($rule in $splitRules) {
        $rulectArr = $rule.split(":")
        $processedContent += @"
    <Rule>
        <Group ID>$($rulectArr[1])</Group ID>
#...
"@

Требуемый вывод:

<GroupId>V-96197</GroupId>
<GroupTitle>SRG-APP-000026-NDM-000208</GroupTitle>
<RuleId>SV-105335r1_rule</RuleId>

Фактический вывод:

<GroupId>V-96197</GroupId>
<GroupTitle></GroupTitle>
<RuleId></RuleId>
...
<GroupId>SRG-APP-000026-NDM-000208</GroupId>
<GroupTitle></GroupTitle>
<RuleId></RuleId>

Я также пытался использовать регулярные выражения без особого успеха (хотя я будусначала признать, что я очень плохой в этом.)

Ответы [ 2 ]

2 голосов
/ 24 октября 2019

Краткое решение:

((Get-Content -Raw raw.txt) -split '\r?\n_+\r?\n\r?\n') | ForEach-Object { 
  @"
<Rule>
$(
  $(foreach ($line in $_ -split '\r?\n' -ne '') {
    $name, $value = $line -split '(?: \(.+?\))?: +'
    $name = $name -replace ' '
    "  <$name>$value</$name>"
  }) -join "`n"
)
</Rule>
"@
}

При вводе образца приведенные выше значения:

<Rule>
  <GroupID>V-96855</GroupID>
  <GroupTitle>SRG-NET-000018-RTR-000001</GroupTitle>
  <RuleID>SV-105993r1_rule</RuleID>
  <Severity>CAT II</Severity>
  <RuleVersion>CISC-RT-000010</RuleVersion>
  <RuleTitle>The Cisco router must be configured to enforce approved authorizations for controlling the flow of information within the network based on organization-defined information flow control policies.</RuleTitle>
  <...></...>
</Rule>
<Rule>
  <GroupID>V-96857</GroupID>
  <GroupTitle>SRG-NET-000025-RTR-000020</GroupTitle>
  <RuleID>SV-105995r1_rule</RuleID>
  <Severity>CAT II</Severity>
  <RuleVersion>CISC-RT-000020</RuleVersion>
  <RuleTitle>The Cisco router must be configured to implement message authentication for all control plane protocols.</RuleTitle>
  <...></...>
</Rule>
<Rule>
  <GroupID>V-96859</GroupID>
  <GroupTitle>SRG-NET-000025-RTR-000085</GroupTitle>
  <RuleID>SV-105997r1_rule</RuleID>
  <Severity>CAT II</Severity>
  <RuleVersion>CISC-RT-000030</RuleVersion>
  <RuleTitle>The Cisco router must be configured to use keys with a duration not exceeding 180 days for authenticating routing protocol messages.</RuleTitle>
  <...></...>
</Rule>

Объяснение:

  • -split '\r?\n_+\r?\n\r?\n' разбивает все содержимое входного файла на блоки строк по строкам разделителя (___...)

  • Затем в вызове ForEach-Object используется расширяемая здесь строка (@"<newline>...") длясоздать элемент <Rules> из отдельных строк в блоке:

    • $_ -split '\r?\n' -ne '' разбивает каждый блок на отдельные строки, отфильтровывая пустые строки.

    • $name, $value = $line -split '(?: \(.+?\))?: +' разделяет каждую строку на имя и значение с помощью разделителя :, за которым следуют еще один пробел, необязательно предшествующий подстроке в (...)

    • $name = $name -replace ' ' removeвсе пробелы в имени.

    • Расширяемая строка " <$name>$value</$name>" создает элемент XML для текущей строки.

    • -join "`n" объединяет всеСтроки XML-элементов с символами новой строки;если вы хотите использовать новую строку, соответствующую платформе, вместо "`n" (только LF), используйте [Environment]::NewLine

1 голос
/ 24 октября 2019

Я думаю, что мой подход был бы немного другим:

  1. Читать каждую строку в;
  2. Если строка содержит символ :, попробуйте разобрать его в ключ-значить пару динамически (вместо жесткого кодирования каждого поля) и сохранить это до более позднего времени;
  3. Если строка _____________________________________________________________, извлечь все сохраненные пары ключ-значение;
  4. Если ни один из приведенных выше вариантов не соответствует действительности, ничего не делайте;
  5. После прочтения всех строк удалите все оставшиеся пары ключ-значение.

Что-то похожее на это:

# A container to hold all of the items:
$items = New-Object 'System.Collections.Generic.List[object]'
# Each item will be a series of key-value pairs in this dictionary:
$item = New-Object 'System.Collections.Generic.Dictionary[string,string]'
Get-Content C:\Temp\linex.txt |% {
  if ($_ -like '*:*') {
    # This is a new key-value pair.
    $linesplit = $_ -split ':'
    # Take out anything in parentheses (), plus any spaces, to make the field name.
    $fieldname = $linesplit[0] -replace '\([^)]*\)', '' -replace ' ',''
    # Since the field contents may contain ':' characters, join them back together.
    # The trim has to happen after the join (so ": " doesn't become ":").
    $item[$fieldname] = ($linesplit[1..($linesplit.Count-1)] -join ':').Trim()
  } elseif ($_ -eq '_____________________________________________________________') {
    # Found an item separator; add this item to the bucket of items.
    $items.Add($item)
    # Prepare the dictionary to receive the new item.
    $item.Clear()
  }
}
# Read all of the lines; if there are any kvp left, add them to the list, too.
if ($item.Count -gt 0) {
  $items.Add($item)
  Remove-Variable item
}

На данный момент есть переменная $items со всеми правилами. Теперь, чтобы получить это в XML. Запись XML по умолчанию немного уродлива, поэтому вы можете использовать что-то вроде этого для создания:

"<Rules>`r`n $($items |% {
  " <Rule>`r`n $(
    foreach ($key in $_.Keys) {
      "    <$key>$($_[$key])</$key>`r`n"
    }
  )  </Rule>`r`n"
})</Rules>"

Это дает:

<Rules>
  <Rule>
     <GroupID>V-96859</GroupID>
     <GroupTitle>SRG-NET-000025-RTR-000085</GroupTitle>
[...]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...