So I've been working on an XML file that has a folder structure stored in it and I want to be able to validate that the folder structure exists in the file system. While there are many ways to try and do this "Resolve-Path" "Get-ChildItems" etc ... I want to be able to build the path that's in the XML structure and put it into a string or something to I can just run Test-Path against it. Sounds easy enough right? Well probably for some seasoned Powershell veterans it might be but I'm new to PS and having some difficulty wrapping my head around this.
所以我一直在研究一个存储有文件夹结构的XML文件,我希望能够验证文件系统中是否存在文件夹结构。虽然有很多方法可以尝试执行此操作“Resolve-Path”“Get-ChildItems”等...我希望能够构建XML结构中的路径并将其放入字符串或其他内容中,我可以运行Test-Path对它。听起来很容易吧?可能对于一些经验丰富的Powershell老兵来说可能是这样,但我是PS的新手并且在我的脑袋周围有些困难。
Here's a sample of XML with the Directory Structure
这是一个带有目录结构的XML示例
<config>
<local>
<setup>
<folder name="FolderA" type="root">
<folder name="FolderC"/>
<folder name="FolderB">
<folder name="FolderD" type="thisone"/>
</folder>
</folder>
</setup>
</local>
</config>
If the child node was set to FolderD the ultimate goal is to build a string that resembles something like this
如果子节点设置为FolderD,则最终目标是构建类似于此类似的字符串
FolderA\FolderB\FolderD
It seems obvious to me that in order to get the full path you'd have to start at the end and walk the node tree backwards, and right there I get lost. The intention is to select only one path out of the multiple ones that potentially could exist. So for instance I want to check the path for FolderD. So I'd have to build just that path \FolderA\FolderB\FolderD and ignore the rest.
对我来说,似乎很明显,为了获得完整的路径,你必须从最后开始并向后走节点树,然后就在那里迷路了。目的是仅从可能存在的多个路径中选择一个路径。所以我想检查FolderD的路径。所以我必须构建那个路径\ FolderA \ FolderB \ FolderD并忽略其余的。
2 个解决方案
#1
3
The simplest approach is probably:
最简单的方法可能是:
- identify the target leaf node
- walk up the hierarchy in a loop and build the path from the
name
attribute values until thetype=root
node is hit
识别目标叶节点
在循环中向上遍历层次结构并从名称属性值构建路径,直到命中type = root节点
$xmlDoc = [xml] @'
<config>
<local>
<setup>
<folder name="FolderA" type="root">
<folder name="FolderC"/>
<folder name="FolderB">
<folder name="FolderD" type="thisone"/>
</folder>
</folder>
</setup>
</local>
</config>
'@
# Get the leaf element of interest.
$leafNode = $xmlDoc.SelectSingleNode('//folder[@type="thisone"]')
# Simply build up the path by walking up the node hierarchy
# until an element with attribute type="root" is found.
$path = $leafNode.name
$node = $leafNode
while ($node.type -ne 'root') {
$node = $node.ParentNode
$path = $node.name + '\' + $path #'# (ignore this - fixes syntax highglighting)
}
# Output the resulting path:
# FolderA\FolderB\FolderD
$path
Original answer, based on the original form of the question: May still be of interest with respect to building paths from element attributes top-down, using recursion.
原始答案,基于问题的原始形式:对于使用递归从上到下的元素属性构建路径,可能仍然感兴趣。
With recursion, you can still employ a top-down approach:
通过递归,您仍然可以采用自上而下的方法:
$xmlDoc = [xml] @'
<folder name="FolderA">
<folder name="FolderC"/>
<folder name="FolderB">
<folder name="FolderD"/>
</folder>
</folder>
'@
# Define a function that walks the specified XML element's hierarchy to
# down its leaf elements, building up a path of the values of the
# specified attribute, and outputting the path for each leaf element.
function get-LeafAttribPaths($xmlEl, $attrName, $parentPath) {
$path = if ($parentPath) { join-path $parentPath $xmlEl.$attrName } else
{ $xmlEl.$attrName }
if ($xmlEl.ChildNodes.Count) { # interior element -> recurse over children
foreach ($childXmlEl in $xmlEl.ChildNodes) {
get-LeafAttribPaths $childXmlEl $attrName $path
}
} else { # leaf element -> output the built-up path
$path
}
}
# Invoke the function with the document element of the XML document
# at hand and the name of the attribute from whose values to build the path.
$paths = get-LeafAttribPaths $xmlDoc.DocumentElement 'name'
# Output the resulting paths.
# With the above sample input:
# FolderA\FolderC
# FolderA\FolderB\FolderD
$paths
# Test paths for existence.
Test-Path $paths
#2
2
I would write it that way:
我会这样写:
$xmlDoc = [xml]'<folder name="FolderA">
<folder name="FolderC"/>
<folder name="FolderB">
<folder name="FolderD"/>
</folder>
</folder>'
function Get-XmlPath($node, $pathPrefix){
$children = $node.SelectNodes('folder[@name]')
$path = [System.IO.Path]::Combine($pathPrefix, $node.name)
if ($children.Count) {
foreach($child in $children) {
Get-XmlPath $child $path
}
} else {
$path
}
}
Get-XmlPath $xmlDoc.DocumentElement | % { [PSCustomObject]@{Path = $_; Exist = Test-Path $_ } }
In my system it yields:
在我的系统中它产生:
Path Exist
---- -----
FolderA\FolderC False
FolderA\FolderB\FolderD False
Updated to reflect new xml structure
更新以反映新的xml结构
You can narrow search to selected node as follows:
您可以将搜索范围缩小到所选节点,如下所示
Get-XmlPath $xmlDoc.SelectSingleNode('//folder[@type="root"]') | % { [PSCustomObject]@{Path = $_; Exist = Test-Path $_ } }
The key is to start from correct node. You can try other selectors as well if this don't suit:
关键是从正确的节点开始。如果不适合,您也可以尝试其他选择器:
#start from first folder node
$xmlDoc.SelectSingleNode('//folder')
#start from first folder node with root attribute
$xmlDoc.SelectSingleNode('//folder[@type="root"]')
#1
3
The simplest approach is probably:
最简单的方法可能是:
- identify the target leaf node
- walk up the hierarchy in a loop and build the path from the
name
attribute values until thetype=root
node is hit
识别目标叶节点
在循环中向上遍历层次结构并从名称属性值构建路径,直到命中type = root节点
$xmlDoc = [xml] @'
<config>
<local>
<setup>
<folder name="FolderA" type="root">
<folder name="FolderC"/>
<folder name="FolderB">
<folder name="FolderD" type="thisone"/>
</folder>
</folder>
</setup>
</local>
</config>
'@
# Get the leaf element of interest.
$leafNode = $xmlDoc.SelectSingleNode('//folder[@type="thisone"]')
# Simply build up the path by walking up the node hierarchy
# until an element with attribute type="root" is found.
$path = $leafNode.name
$node = $leafNode
while ($node.type -ne 'root') {
$node = $node.ParentNode
$path = $node.name + '\' + $path #'# (ignore this - fixes syntax highglighting)
}
# Output the resulting path:
# FolderA\FolderB\FolderD
$path
Original answer, based on the original form of the question: May still be of interest with respect to building paths from element attributes top-down, using recursion.
原始答案,基于问题的原始形式:对于使用递归从上到下的元素属性构建路径,可能仍然感兴趣。
With recursion, you can still employ a top-down approach:
通过递归,您仍然可以采用自上而下的方法:
$xmlDoc = [xml] @'
<folder name="FolderA">
<folder name="FolderC"/>
<folder name="FolderB">
<folder name="FolderD"/>
</folder>
</folder>
'@
# Define a function that walks the specified XML element's hierarchy to
# down its leaf elements, building up a path of the values of the
# specified attribute, and outputting the path for each leaf element.
function get-LeafAttribPaths($xmlEl, $attrName, $parentPath) {
$path = if ($parentPath) { join-path $parentPath $xmlEl.$attrName } else
{ $xmlEl.$attrName }
if ($xmlEl.ChildNodes.Count) { # interior element -> recurse over children
foreach ($childXmlEl in $xmlEl.ChildNodes) {
get-LeafAttribPaths $childXmlEl $attrName $path
}
} else { # leaf element -> output the built-up path
$path
}
}
# Invoke the function with the document element of the XML document
# at hand and the name of the attribute from whose values to build the path.
$paths = get-LeafAttribPaths $xmlDoc.DocumentElement 'name'
# Output the resulting paths.
# With the above sample input:
# FolderA\FolderC
# FolderA\FolderB\FolderD
$paths
# Test paths for existence.
Test-Path $paths
#2
2
I would write it that way:
我会这样写:
$xmlDoc = [xml]'<folder name="FolderA">
<folder name="FolderC"/>
<folder name="FolderB">
<folder name="FolderD"/>
</folder>
</folder>'
function Get-XmlPath($node, $pathPrefix){
$children = $node.SelectNodes('folder[@name]')
$path = [System.IO.Path]::Combine($pathPrefix, $node.name)
if ($children.Count) {
foreach($child in $children) {
Get-XmlPath $child $path
}
} else {
$path
}
}
Get-XmlPath $xmlDoc.DocumentElement | % { [PSCustomObject]@{Path = $_; Exist = Test-Path $_ } }
In my system it yields:
在我的系统中它产生:
Path Exist
---- -----
FolderA\FolderC False
FolderA\FolderB\FolderD False
Updated to reflect new xml structure
更新以反映新的xml结构
You can narrow search to selected node as follows:
您可以将搜索范围缩小到所选节点,如下所示
Get-XmlPath $xmlDoc.SelectSingleNode('//folder[@type="root"]') | % { [PSCustomObject]@{Path = $_; Exist = Test-Path $_ } }
The key is to start from correct node. You can try other selectors as well if this don't suit:
关键是从正确的节点开始。如果不适合,您也可以尝试其他选择器:
#start from first folder node
$xmlDoc.SelectSingleNode('//folder')
#start from first folder node with root attribute
$xmlDoc.SelectSingleNode('//folder[@type="root"]')