0

I have this working script, but I think it could be done cleaner. So, to start with a little background..

I'm receiving an XML file from another company. The file contains a header, then all of the XML without any CRLF characters, then a trailer. We're uploading this file to a z/OS mainframe and it doesn't like the one incredibly long line.

I created this script to remove the header and trailer, reformat the XML and then put the header and trailer back on. It works, but at one point in the script I have to write a temporary file and then read the temporary file back in, which seems inefficient to me.

Anyone have any suggestions for getting rid of the "Temp_2_DAY2FileOUT" file in the script below:

############# CODE BEGINS #############

## Count the number of files matching the file mask provided
$x = ( Get-ChildItem "\\corp\dfs\Transfer\GAB\*DAY2FileOUT" | Measure-Object ).Count

## If the number of files from the previous command is not equal to 1 run the command with the verbose parameter to populate the log with the command results,
## then terminate with a bad exit code (indicating a failure)
If($x -NE 1){
    Get-ChildItem "\\corp\dfs\Transfer\GAB\*DAY2FileOUT" -verbose
    Exit 8}

## Read the file into a variable, named input, as a Powershell Object
$input = Get-Content "\\corp\dfs\Transfer\GAB\*DAY2FileOUT"
## Read the first record of the input variable into a new variable, named header, as a Powershell Object
$header = $input[0..0]
## Read all but the first and last record of the input variable into a new variable, named content, as a Powershell Object
$content = $input[1..($input.count - 2)]
## Read all the last record of the input variable into a new variable, named trailer, as a Powershell Object
$trailer = $input[($input.count - 1)..($input.count - 1)]

## Write the content variable out to a temporary file so that we have a file without the header and trailer records
$xml = [xml] $content
$xml.Save("\\corp\dfs\Transfer\GAB\Temp_2_DAY2FileOUT")

##  Write the header record to the final output file
$header | Out-File "\\corp\dfs\Transfer\GAB\DAY2FileOUT.xml" -Encoding ascii

##  Append the contents of the temproary file to the final output file
(Get-Content "\\corp\dfs\Transfer\GAB\Temp_2_DAY2FileOUT") | Out-File -FilePath "\\corp\dfs\Transfer\GAB\DAY2FileOUT.xml" -Encoding ascii -Append

## Append the trailer record to the final output file
$trailer | Out-File "\\corp\dfs\Transfer\GAB\DAY2FileOUT.xml" -Encoding ascii -Append

##  Delete the input file and the temporary file
#Remove-Item "\\corp\dfs\Transfer\GAB\*DAY2FileOUT"

## Terminate with a success status
Exit 0
############## CODE ENDS ##############
Greg
  • 11
  • 1
  • Hi, welcome to stack overflow. Please refer the [ask] link for more details on how to ask a question and update your question accordingly. – Jeroen Heier Feb 14 '18 at 21:00

1 Answers1

0

You can use the Save() method to store the information in a variable instead of a file. There's also pretty of room for making the code more readable and easier to maintain.

I've used TechSpud's helpful answer in putting this together. Instead of switching to the WriteTo() method you can continue to use Save(). It has an override to accept a TextWriter instead of a filepath string.

You first need to create a StringWriter object, then an XmlTextWriter object.

############# CODE BEGINS #############

# Use variables instead of hardcoding file paths. Easier to update
$Path            = "\\corp\dfs\Transfer\GAB\*DAY2FileOUT"
$formattedFile   = "\\corp\dfs\Transfer\GAB\DAY2FileOUT.xml"

# $x - no need to create a variable you're only going to use once.
# Get-ChildItem already has a Count property - Measure-Object is redundant
if((Get-ChildItem $Path).Count -ne 1){
    Get-ChildItem $Path -Verbose
    Exit 8
}

# avoid $input - see further explanation
$xmlContent = Get-Content $Path
$xmlPart    = [xml] ($xmlContent[1..($xmlContent.count - 2)])

# Thanks to TechSpud.
$xmlString = New-Object System.IO.StringWriter
$writer    = New-Object System.Xml.XmlTextwriter($xmlString)
$writer.Formatting = [System.XML.Formatting]::Indented     # see further explanation

# Save to variable instead of file
$xmlPart.WriteTo($writer)

# Put the pieces together and export the file in one go.
# .. means you are specifying a range. not required if you only want 1 element
# -1 automatically returns the last entry
$xmlContent[0] + "`n" + $xmlString.ToString() + "`n" + $xmlContent[-1] |
   Out-File $formattedFile -Encoding ascii

Exit 0
############## CODE ENDS ##############

Further explanation

  • $input is an automatic variable. The script ran fine for me but it can lead to problems later on. Pasting the lines into a console will not work because it will not allow you to overwrite the automatic $input variable.

  • That line specifies Formatting should be the default Indented Formatting - essentially the character (default space) and number of them (default:2).


Edit

`n - new line
`r`n - carriage return, new line.
G42
  • 9,791
  • 2
  • 19
  • 34
  • Thank you, very much, gms0ulman. This code is much cleaner! I'm having one minor issue with it, though. It's not putting a crlf between the header and the first line of xml, or between the last line of xml and the trailer. – Greg Feb 20 '18 at 13:30
  • @Greg Please see edit - tried to do it comments but it was badly formatted – G42 Feb 20 '18 at 13:51
  • It turns out, there is a LF character between the header and the xml and a LF character between the xml and the trailer. I'm pushing this file to UNIX system, so the LF is working just fine for me. Thank you again for your help. It's very much appreciated. – Greg Feb 20 '18 at 14:20
  • @Greg Good to hear. If you've found this rssolved your issue, please consider [accepting/upvoting](https://stackoverflow.com/help/someone-answers) – G42 Feb 20 '18 at 16:06