Reputation: 892
I have 3 folders with some files and 3 extensions inside. I want to count the total of the files for each folder, then I want to write the output to an XML file. I tried this, but I can only write the output to an XML file for one folder.
$FindFolder = Get-ChildItem -Directory "D:\"
foreach ($folder in $FindFolder) {
$jpg = Get-ChildItem -Name "D:\$folder" -Recurse -File -Include *.jpg |
Measure-Object |
ForEach-Object {$_.Count}
$png = Get-ChildItem -Name "D:\$folder" -Recurse -File -Include *.png |
Measure-Object |
ForEach-Object {$_.Count}
$gif = Get-ChildItem -Name "D:\$folder" -Recurse -File -Include *.gif |
Measure-Object |
ForEach-Object {$_.Count}
# Write to xml file
$Output = @"
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="Tool.css"?>
<report>
<heading>Final Report</heading>
<foldername>$folder</foldername>
<jpg>$jpg</jpg>
<png>$png</png>
<gif>$gif</gif>
</report>
"@
$Output | Out-File "D:\Reports.xml" -NoNewline -Force
}
My output with this script above is:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="Tool.css"?>
<report>
<heading>Final Report</heading>
<foldername>folder1</foldername>
<jpg>11</jpg>
<png>11</png>
<gif>0</gif>
</report>
The expected output is:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="Tool.css"?>
<report>
<heading>Final Report</heading>
<foldername>folder1</foldername>
<jpg>11</jpg>
<png>11</png>
<gif>0</gif>
<foldername>folder2</foldername>
<jpg>1</jpg>
<png>18</png>
<gif>9</gif>
<foldername>folder3</foldername>
<jpg>16</jpg>
<png>13</png>
<gif>1</gif>
</report>
The total of <foldername>NN</foldername>
is depend on how many folder I have.
Upvotes: 0
Views: 346
Reputation: 200493
The code you posted should only give you the result of the last folder, since you overwrite the output file with every iteration. Also, the XML structure you chose is terrible, as it relies on the order of elements instead of establishing a proper hierarchical structure. This would be significantly better:
<?xml version="1.0" encoding="UTF-8"?>
<report>
<heading>Final Report</heading>
<folders>
<folder name="folder1">
<jpg>11</jpg>
<png>11</png>
<gif>0</gif>
</folder>
<folder name="folder2">
<jpg>1</jpg>
<png>18</png>
<gif>9</gif>
</folder>
<folder name="folder3">
<jpg>16</jpg>
<png>13</png>
<gif>1</gif>
</folder>
</folders>
</report>
The simple solution is to write the top section of your output file before the loop, the trailing section after the loop, and only write the folder sections inside the loop:
@'
<?xml version="1.0" encoding="UTF-8"?>
<report>
<heading>Final Report</heading>
<folders>
'@ | Set-Content $report -Encoding UTF8
foreach ($folder in $FindFolder) {
...
@"
<folder name="folder2">
<jpg>$jpg</jpg>
<png>$png</png>
<gif>$gif</gif>
</folder>
"@ | Add-Content $report -Encoding UTF8
}
@'
</folders>
</report>
'@ | Add-Content $report -Encoding UTF8
A better approach would be to construct a proper XML object, e.g. like this:
$report = 'D:\Reports.xml'
$xml = New-Object Xml.XmlDocument
$preamble = $xml.CreateXmlDeclaration('1.0', 'utf-8', $null)
$xml.InsertBefore($preamble, $xml.DocumentElement) | Out-Null
$root = $xml.CreateElement('report')
$xml.AppendChild($root) | Out-Null
$folders = $xml.CreateElement('folders')
$xml.DocumentElement.AppendChild($folders) | Out-Null
and insert the <folder>
nodes in the loop:
foreach ($folder in $FindFolder) {
$f = $xml.CreateElement('folder')
$a = $xml.CreateAttribute('name')
$a.Value = $folder.Name
$f.Attributes.Append($a)
$jpg = $xml.CreateElement('jpg')
$jpg.InnerText = [string](Get-ChildItem $folder.FullName -Recurse -File -Filter '*.jpg').Count
$f.AppendChild($jpg) | Out-Null
...
$folders.AppendChild($f)
}
Then save the XML document:
$xml.Save($report)
Upvotes: 2
Reputation: 246
I think what you need is to split up the work into three parts - header data, looping inner data, and any footer data, then just use -Append
to add to the file:
$mainFolder = "D:"
# Initialise file with header information
@"
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="Tool.css"?>
<report>
<heading>Final Report</heading>
"@ | Out-File "$mainFolder\Reports.xml" -Force
# Loop over the folders
$FindFolder = Get-ChildItem -Directory $mainFolder
foreach ($folder in $FindFolder) {
$jpg = (Get-ChildItem -Name "$mainFolder\$folder" -Recurse -File -Include *.jpg).Count
$png = (Get-ChildItem -Name "$mainFolder\$folder" -Recurse -File -Include *.png).Count
$gif = (Get-ChildItem -Name "$mainFolder\$folder" -Recurse -File -Include *.gif).Count
# Append individual folder report to file
@"
<foldername>$folder</foldername>
<jpg>$jpg</jpg>
<png>$png</png>
<gif>$gif</gif>`n
"@ | Out-File "$mainFolder\Reports.xml" -Force -Append
}
# Append footer information to file
@"
</report>
"@ | Out-File "$mainFolder\Reports.xml" -NoNewline -Force -Append
In the above, I've extracted out the main folder into a variable (just to simplify slightly), then initialised the file with header information. It then loops over each folder, and adds the report parts into the file. Finally, it completes the file. I've changed the line breaks a little (left out the -NoNewLine
from the first two writes, and added a line break in the loop - you maybe like to change the formatting to better suit your needs.
Upvotes: 2