【问题标题】:Select-String from pattern A to pattern B从模式 A 到模式 B 的 Select-String
【发布时间】:2015-08-04 12:23:30
【问题描述】:

有没有办法使用Select-String 来查找XY 之间的所有行。

例如如果我有一个包含内容的文件:

[line 157: Time 2015-08-04 11:34:00] 
<staff>
    <employee>
        <Name>Bob Smith</Name>
        <function>management</function>
        <age>39</age>
        <birthday>3rd June</birthday>
        <car>yes</car>
    </employee>
    <employee>
        <Name>Sam Jones</Name>
        <function>security</function>
        <age>24</age>
    </employee>
    <employee>
        <Name>Mark Perkins</Name>
        <function>management</function>
        <age>32</age>
    </employee>
</staff>

我想找到&lt; function &gt;management&lt; /function &gt; 的所有内容,所以我最终会得到:

<employee>
    <Name>Bob Smith</Name>
    <function>management</function>
    <age>39</age>
    <birthday>3rd June</birthday>
    <car>yes</car>
</employee>
<employee>
    <Name>Mark Perkins</Name>
    <function>management</function>
    <age>32</age>
</employee>    

如果所有分组的大小相同,我可以使用类似:

Select-String -Pattern '<function>management</function>' -CaseSensitive -Context 2,2

但是,实际上它们的大小不会相同,所以我不能每次都使用固定的数字。

真的,我需要一种方式来表示返回所有内容:

2 rows above my search term
until
the following '</employee>' field

对于所有匹配的实例。

这可能吗?

我不能在 powershell 中使用标准的 xml 工具,因为我正在阅读的文件不是标准的 xml,因此我以 [line 157: Time 2015-08-04 11:34:00] 为例。最好的想法是大量的 xml 文件,全部合并到一个 xml 文件中,并使用 [line . . .] 标头将它们分解。


其他信息: 我担心我的例子有点过于简单,实际文件更像:

[line 157: Time 2015-08-04 11:34:00]
<?xml version="1.0" encoding="utf-8"?>
<other>
    <stuff>
    . . .
    </stuff>
</other>

<?xml version="1.0" encoding="utf-8"?>
<staff>
    <employee>
    ...
    </employee>
</staff> 

<staff>
    <employee>
    ...
    </employee>
</staff>
[line End: Time 2015-08-04 11:34:00]

其他信息 我添加了代码以忽略 &lt; ?xml version. . . 行。 我还尝试添加我自己的根元素:

$first = "<open>"
$last = "</open>"
$a = 0

. . .

if($a -eq 0)
    {
        $XmlFiles[$Index] += $first
        $a++
    } 

. . .

$XmlFiles[$Index] += $last

但这会产生Array assignment failed because index '-1' was out of range. 错误


其他信息 最终结果是这样的:

$FilePath = "C:\Path\To\XmlDocs.txt"
$XmlFiles = @()
$Index = -1

$first = "<open>"
$last = "</open>"

# Go through the file and store the individual xml documents in a string array
$a=0
Get-Content $FilePath | `
%{
    if($_ -match "^\[line\ \d+")
        {
            if($a -eq 0)
                {
                    #if this is the top line, ignore it
                }
            else
                {
                    #if this is a boundary, add a closing < /open > tag
                    $XmlFiles[$Index] += $last
                }
            # We've got a boundary, move to next index in array
            $Index++
            # Add a new string to hold the next xml document
            $XmlFiles += ""
            # Add an < open > tag
            $XmlFiles[$Index] += $first
            $a++
        } 
    elseif ($_ -match '^\<\?xml') #ignore xml headers
        {
            # End of Section, or XML Header. Do Nothing and move on
        }
    elseif([string]::IsNullOrEmpty($_))
        {
            # Blank Line, Do Nothing and move on
        }
    else 
        {
            # Add each line to the string (xml doesn't care about line breaks)
            $XmlFiles[$Index] += $_
        }
}

# add the final < /open > tag
$XmlFiles[$Index] += $last

$a=0
$Results = foreach($File in $XmlFiles)
{
    $Xml = [xml]($File.Trim())
    # Parse string as an Xml document
    $Xml = [xml]$File
    # Use Xpath to find the manager
    $Xml.SelectNodes("//employee[function = 'management']") |% {$_}
    $a++
}

$Results

它基本上忽略了标题[line. . .、xml 定义&lt; ?xml 和任何空行,并在每个部分周围添加了&lt; open &gt;. . . &lt; /open &gt; 标记以使其有效。

【问题讨论】:

  • 每个文件中是否只有一对[line ...] [line End ...]?如果是这样,只需丢弃 [] 行并在遇到空行时增加索引
  • 很遗憾没有,添加了许多 [line] 对。这基本上是一个日志文件,每隔几分钟就会写入一次。
  • 修复损坏的输入文件格式。说真的。
  • 我希望我可以,不幸的是我没有构建它。
  • @IGGt 你真的试过更新我的例子中的 if 语句来做if(($_ -match "^\[line\ \d+") -or [string]::IsNullOrWhiteSpace($_))吗?

标签: xml powershell-2.0 select-string


【解决方案1】:

我认为您高估了将单个 Xml 文档解析为实际 XML 的挑战。您可以逐行阅读文件,并使用“[line ...]”字符串作为各个文档之间的边界:

$FilePath = "C:\Path\To\XmlDocs.txt"
$XmlFiles = @()
$Index = -1

# Go through the file and store the individual xml documents in a string array
Get-Content $FilePath |%{
    if($_ -match "^\[line\ \d+"){
        # We've got a boundary, move to next index in array
        $Index++
        # Add a new string to hold the next xml document
        $XmlFiles += ""
    } else {
        # Add each line to the string (xml doesn't care about line breaks)
        $XmlFiles[$Index] += $_
    }
}

$Managers = foreach($File in $XmlFiles){
    # Parse string as an Xml document
    $Xml = [xml]$File
    # Use Xpath to find the manager
    $Xml.SelectNodes("//employee[function = 'management']") |% {$_}
}

使用这样的示例文件(示例的修改/扩展版本):

[line 157: Time 2015-08-04 11:34:00] 
<staff>
    <employee>
        <Name>Bob Smith</Name>
        <function>management</function>
        <age>39</age>
        <birthday>3rd June</birthday>
        <car>yes</car>
    </employee>
    <employee>
        <Name>Sam Jones</Name>
        <function>security</function>
        <age>24</age>
    </employee>
    <employee>
        <Name>Mark Perkins</Name>
        <function>management</function>
        <age>32</age>
    </employee>
</staff>
[line 158: Time 2015-08-06 12:36:30] 
<staff>
    <employee>
        <Name>Rob Smith</Name>
        <function>management</function>
        <age>39</age>
        <birthday>3rd June</birthday>
        <car>yes</car>
    </employee>
    <employee>
        <Name>Cam Jones</Name>
        <function>security</function>
        <age>24</age>
    </employee>
    <employee>
        <Name>Stark Perkins</Name>
        <function>management</function>
        <age>32</age>
    </employee>
</staff>

生成的$Managers 将是:

PS C:\> $Managers|Select Name,function,age

Name                               function                          age
----                               --------                          ---
Bob Smith                          management                        39
Mark Perkins                       management                        32
Rob Smith                          management                        39
Stark Perkins                      management                        32

【讨论】:

  • 谢谢你,我可以看到它的原理,但我不能让它工作。我担心我上面的例子有点过于简单化了。由于每个部分都有自己的 XML 声明,我收到了 Unexpected XML declaration 错误。从我的测试数据中删除它们会给我There are multiple root elements 错误。
  • @IGGt 这应该不是问题,但这可能是由于 XML 声明前面有一些前面的空格。在foreach() 循环中尝试$Xml = [xml]($File.Trim())
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-08
相关资源
最近更新 更多