Answered Need a way to shuffle data around

  • Friday, January 18, 2013 8:03 PM
     
      Has Code

    I have a script that queries HBA information on servers and outputs that data into a text file for each server. The powershell script launches psexec on the remote server that calls a batch file with the HBA query commands. The batch file redirects the output to a text file locally. The text file is then copied to a network share. There is one text file per server. This script works well. Here is the main script:

    $SrvList = "C:\batch\dan\servers_main.lst"
    $server = Get-Content $SrvList
    $exe = "C:\batch\dan\bin\psexec.exe"
    $LogLoc = "\\<serverName>\logs\HBA Info\"
    $LogFile = $LogLoc + "Execution_Results.log"
    $DateAndSuch = Date
    # Make sure we can move forward
    if (!(Test-Path $LogLoc)) 
    {
        New-Item $LogLoc -type directory
    }
    # Initialize and create log file
    Write-Output "$DateAndSuch -- Initializing HBA Gather script" >> $LogFile
    if (!(Test-Path $exe)) 
    {
        Write-Output "$DateAndSuch -- psexec.exe not found!" >> $LogFile
        Write-Output "----END OF EXECUTION----" >> $LogFile
        exit
    }
    if (!(Test-Path $SrvList)) 
    {
        Write-Output "$DateAndSuch -- server.lst not found!" >> $LogFile
        Write-Output "----END OF EXECUTION----" >> $LogFile
        exit
    }
    # Set up shell
    $Host.UI.RawUI.WindowTitle = HBA Gather Script"
    clear
    Write-Output "$DateAndSuch -- Initialization complete" >> $LogFile
    # Loop through server list and gather HBA data
    foreach ($i in $server)
    {   
        # call date variable again, to get accurate timestamp for this iteration
        $DateAndSuch = Date
        Write-Output "Gathering HBA data from $i"
        Write-Output "$DateAndSuch -- HBA data from $i" >> $LogFile
        $params = '\\'+$i+' -u <userID> -p <password> -d cmd.exe /c "\\<batchServer>\C$\batch\dan\bin\hba.bat"'
        Write-Output $params
    	$execResult = (Start-Process -FilePath $exe -ArgumentList $params -Wait -PassThru).ExitCode
        $execResult = [int]$execResult
        
        if ($execResult = '0')
        {
            Write-Host "Execution result    " -NoNewline
            Write-Host "$execResult    " -ForegroundColor Green -NoNewline
            Write-Host "on    $i"
        }
        else
        {
            Write-Host "Execution result    " -NoNewline
            Write-Host "$execResult    " -ForegroundColor Red -NoNewline
            Write-Host "on    $i"
        }
        
        Write-Output "$DateAndSuch -- Finished executing HBA data gathering on $i with this return code: $execResult" >> $LogFile
    }
    Write-Output "----END OF EXECUTION----" >> $LogFile
    # Emulate the pause command in old school batch
    Write-Output "Press any key to continue ..."
    $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

    Here is the batch file that runs locally on each server (kicked off by the above script):

    @echo off set logFile="\\serverName\logs\HBA Info\hbaInfo_%computername%.txt" set errlogFile="\\serverName\logs\HBA Info\stdErr\sclistderr_%computername%.txt" set stdlogFile="\\serverName\logs\HBA Info\stdOut\sclistd_%computername%.txt"

    IF EXIST "C:\Program Files (x86)\QLogic Corporation\SANsurferCLI" ( set scliExe="C:\Program Files (x86)\QLogic Corporation\SANsurferCLI\scli.exe" ) ELSE ( set scliExe="C:\Program Files\QLogic Corporation\SANsurferCLI\scli.exe" ) %scliExe% -i > out.txt 2>&1 %scliExe% -c >> out.txt 2>&1 IF %ERRORLEVEL% NEQ 0 GOTO errOut FIND "Host Name" < out.txt >> %logFile% FIND "WWPN" < out.txt >> %logFile% FIND "HBA Model" < out.txt >> %logFile% FIND "HBA Status" < out.txt >> %logFile% FIND "Connection Options" < out.txt >> %logFile% FIND "Actual Connection Mode" < out.txt >> %logFile% FIND "Data Rate :" < out.txt >> %logFile% FIND "Actual Data Rate" < out.txt >> %logFile% FIND "Execution Throttle" < out.txt >> %logFile% FIND "Port Down Retry Count" < out.txt >> %logFile% FIND "Link Down Timeout" < out.txt >> %logFile% GOTO end :errOut copy out.txt %errlogFile% del out.txt del QLInitLog.txt :end copy out.txt %stdlogFile% del out.txt del QLInitLog.txt


    Example output of one of the text files:

    --------------------------------------------------------------------

    Host Name                      : <serverName>
    Host Name                      : <serverName>
    HBA Instance                   : 0
    HBA Instance                   : 1
    Port ID                        : <IDNum>
    Port ID                        : <IDNum>
    HBA Port                       : 1
    HBA Port                       : 2
    Port Name                      : <HBA WWN>
    Port Name                      : <HBA WWN>
    HBA Model                      : <model>
    HBA Model                      : <model>
    HBA Status                     : Online
    HBA Status                     : Online
    Connection Options             : 1 - Point-to-Point Only
    Connection Options             : 1 - Point-to-Point Only
    Actual Connection Mode         : Point to Point
    Actual Connection Mode         : Point to Point
    Data Rate                      : 4 Gbps
    Data Rate                      : 4 Gbps
    Actual Data Rate               : 4 Gbps
    Actual Data Rate               : 4 Gbps
    Execution Throttle             : 65535
    Execution Throttle             : 65535
    Port Down Retry Count          : 45
    Port Down Retry Count          : 45
    Link Down Timeout (seconds)    : 45
    Link Down Timeout (seconds)    : 45

    --------------------------------------------------------------------

    Now that I have hundreds of individual text files with the information that I need, I'd like a way to put them all in a .csv so I can sort them etc... Ideally, I'd like to have each Parameter name as a column heading (Server name, WWN, Model, Status etc...).

    Does anyone have any input on a good method of accomplishing this?


    • Edited by Dan Hyman Friday, January 18, 2013 8:05 PM
    •  

All Replies

  • Friday, January 18, 2013 9:43 PM
     
     

    Parse each file into a single C SV line and add to a file.

    This can be easily done with PowerShell using the load data from string CmdLet:
    help  ConvertFrom-StringData -full


    ¯\_(ツ)_/¯

  • Friday, January 18, 2013 11:02 PM
     
      Has Code
    cat stringdata.txt|
         out-string|
         %{$_ -replace ':','='}|
         ConvertFrom-StringData|
         %{new-object PsObject -Prop $_}


    ¯\_(ツ)_/¯

  • Friday, January 18, 2013 11:46 PM
     
     Answered Has Code

    Hi jrv, from the example posted, the 'key' fields are not unique, so you're going to get...

    ConvertFrom-StringData : Data item 'Host Name' in line 'Host Name                      = <serverName>' is already defined.

    I propose this probably over-complicated method instead...

    $testdata=Get-Content 'c:\temp\testdata.txt'
    
    $instancecount=(((Select-String -Inputobject $testdata -Pattern 'HBA Instance' -AllMatches).Matches).Count) -1
    0..$instancecount | ForEach{New-Variable -Name "hash$($_)" -value @{} -force}
    
    $testdata | ForEach{
        $tempname,$tempvalue=$_ -split '\s+:\s'
        For($i=0;$i -le $instancecount;$i++){
            if(!(iex $"hash$($i).ContainsKey('$tempname')")){iex $"hash$($i).Add('$tempname','$tempvalue')";$i=$instancecount}
        }
    }
    
    $output=@()
    For($i=0;$i -le $instancecount;$i++){
        iex $"output+=New-Object PSObject -Property $"hash$($i)
    }
    
    $output | ft 'Host Name','HBA Port','HBA Instance' -a
    
    Result:
    
    Host Name    HBA Port HBA Instance
    ---------    -------- ------------
    <serverName> 1        0           
    <serverName> 2        1           

    Let the agitating begin :-)


    Inspired by Heineken.

    • Marked As Answer by Dan Hyman Monday, January 21, 2013 3:32 PM
    •  
  • Saturday, January 19, 2013 12:16 AM
     
     

    The file posted is in error.  The utility does not produce duplicate lines.  I suspect the OP combined two files and sorted them.  This won't work.  You have to load one file at a time.  The string data converter was made just for this scenario.


    ¯\_(ツ)_/¯

  • Saturday, January 19, 2013 12:28 AM
     
     
    Oh well, it was an interesting exercise in dealing with an unknown number of hash tables.

    Inspired by Heineken.

  • Saturday, January 19, 2013 12:39 AM
     
     
    Oh well, it was an interesting exercise in dealing with an unknown number of hash tables.

    Inspired by Heineken.

    Ha!  I think Dan has gone home for the week.  We will see on Monday.

    Dortmunder Actien Brauerei/DAB (Lager)


    ¯\_(ツ)_/¯

  • Saturday, January 19, 2013 12:41 AM
     
     

    His batch file does appear to be running the command twice into the same output file...

    %scliExe% -i > out.txt 2>&1
    %scliExe% -c >> out.txt 2>&1


    Inspired by Heineken.

  • Saturday, January 19, 2013 12:44 AM
     
     

    His batch file does appear to be running the command twice into the same output file...

    %scliExe% -i > out.txt 2>&1
    %scliExe% -c >> out.txt 2>&1


    Inspired by Heineken.


    Yup - then he sorted it thinking that might help.

    ¯\_(ツ)_/¯

  • Saturday, January 19, 2013 12:50 AM
     
     

    haha.. now we play the waiting game...

    "Dortmunder Actien Brauerei/DAB (Lager)"

    How does it taste? I can only see what the bottle looks like :-)

    http://en.wikipedia.org/wiki/Dortmunder_Actien_Brauerei


    Inspired by Heineken.

  • Saturday, January 19, 2013 12:53 AM
     
     

    Dan - it would be much easier of you set the output option to output XML.  This is structured data and can be loaded and converted very easily.


    ¯\_(ツ)_/¯

  • Saturday, January 19, 2013 12:55 AM
     
     

    haha.. now we play the waiting game...

    "Dortmunder Actien Brauerei/DAB (Lager)"

    How does it taste? I can only see what the bottle looks like :-)

    http://en.wikipedia.org/wiki/Dortmunder_Actien_Brauerei


    Inspired by Heineken.


    If you can get the Octoberfest special brew it is heaven.

    ¯\_(ツ)_/¯

  • Monday, January 21, 2013 2:59 PM
     
     
    Most of the output files do have duplicate lines (such as server name, HBA model etc...) because almost all of my servers have more than one HBA. Most have two but some of my database servers have up to 8.
  • Monday, January 21, 2013 3:00 PM
     
     
    Correct. I run scli with the '-i' switch and run it again with the '-c' switch. They produce different output; I'm extracting data from both and dumping it in the text file.
  • Monday, January 21, 2013 3:01 PM
     
     

    I did play around with sorting data, combining all the text files (cp *.txt hbaInfo_all.txt) etc... to see if the text was easier to work with.

  • Monday, January 21, 2013 3:04 PM
     
     

    Thank you guys for the input so far! This is great stuff. I love the idea of throwing the data into a hash table and then being able to work with the data from there. I'll play with some of the suggestions above and see how it works out. Please keep the suggestions coming; you guys are great!

    Just an FYI; I'm relatively new to powershell and haven't done much with creating custom objects and working with hashtables. I'm open to suggestions on a better method for accomplishing this same goal.

    Thanks again for the help on this!

  • Monday, January 21, 2013 5:31 PM
     
     
    Correct. I run scli with the '-i' switch and run it again with the '-c' switch. They produce different output; I'm extracting data from both and dumping it in the text file.

    You should select the switch that outputs XML.  This can be eawsily converted without a lot of messing around.

    Output as text will not be in pairs of lines but as one list for each HBA.  These are easily separated into two objects.


    ¯\_(ツ)_/¯

  • Monday, January 21, 2013 6:46 PM
     
      Has Code

    I use data that is returend from the -i switch and the -c switch in scli, here is the output from 'scli.exe -c -x':

      <?xml version="1.0" encoding="ISO-8859-1" ?> 
    - <QLogic>
      <AppName>SANsurfer FC/CNA HBA CLI</AppName> 
      <AppVersion>1.7.3.37</AppVersion> 
    - <HBA>
      <HBA Port="0" WWNN="00-00-00-00-00-00-00-00" WWPN="00-00-00-00-00-00-00-00" /> 
      <Param ConnectionOption="1" DataRate="1" FrameSize="2048" HardLoopID="0" LoopResetDelay="5" EnableHostAdapterBIOS="1" EnableHardLoopId="0" FibreChannelTapeSupport="1" ExecutionThrottle="256" LoginRetryCount="8" PortDownRetryCount="45" EnableLipFulllogin="1" LinkDownTimeout="45" EnableTargetReset="1" LUNsPerTarget="256" /> 
      </HBA>
    - <HBA>
      <HBA Port="1" WWNN="00-00-00-00-00-00-00-01" WWPN="00-00-00-00-00-00-00-01" /> 
      <Param ConnectionOption="1" DataRate="1" FrameSize="2048" HardLoopID="0" LoopResetDelay="5" EnableHostAdapterBIOS="1" EnableHardLoopId="0" FibreChannelTapeSupport="1" ExecutionThrottle="256" LoginRetryCount="8" PortDownRetryCount="45" EnableLipFulllogin="1" LinkDownTimeout="45" EnableTargetReset="1" LUNsPerTarget="256" /> 
      </HBA>
      <Status>0</Status> 
      <Reboot>0</Reboot> 
      </QLogic>

    Here is the output from 'scli.exe -i -x':

      <?xml version="1.0" encoding="ISO-8859-1" ?> 
    - <QLogic>
      <AppName>SANsurfer FC/CNA HBA CLI</AppName> 
      <AppVersion>1.7.3.37</AppVersion> 
    - <HBA>
      <GeneralInfo Port="0" Model="XXX0000" WWNN="00-00-00-00-00-00-00-00" WWPN="00-00-00-00-00-00-00-00" PortID="00-00-00" SerialNumber="xxxxx00000" DriverVersion="9.1.7.16" BIOSVersion="1.24" FirmwareVersion="4.03.01" OptionROMBiosVersion="1.24" OptionROMFCodeVersion="1.25" OptionROMEFIVersion="1.08" OptionROMFirmwareVersion="4.00.24" TargetCount="3" PCIBus="8" PCIDevice="1" ActualConnectionMode="Point To Point" ActualDataRate="2 Gbps" PortType="NPort" Status="Online" /> 
      </HBA>
    - <HBA>
      <GeneralInfo Port="1" Model="XXX0000" WWNN="00-00-00-00-00-00-00-01" WWPN="00-00-00-00-00-00-00-01" PortID="00-00-00" SerialNumber="xxxxx00000" DriverVersion="9.1.7.16" BIOSVersion="1.24" FirmwareVersion="4.03.01" OptionROMBiosVersion="1.24" OptionROMFCodeVersion="1.25" OptionROMEFIVersion="1.08" OptionROMFirmwareVersion="4.00.24" TargetCount="3" PCIBus="8" PCIDevice="33" ActualConnectionMode="Point To Point" ActualDataRate="2 Gbps" PortType="NPort" Status="Online" /> 
      </HBA>
      <Status>0</Status> 
      <Reboot>0</Reboot> 
      </QLogic>


    • Edited by Dan Hyman Monday, January 21, 2013 7:21 PM
    •  
  • Monday, January 21, 2013 7:17 PM
     
      Has Code

    Here is what that really looks like as XML and not a screen scrape from IE.

    <?xml version="1.0" encoding="ISO-8859-1" ?> 
    <QLogic>
         <AppName>SANsurfer FC/CNA HBA CLI</AppName> 
         <AppVersion>1.7.3.37</AppVersion> 
         <HBA>
             <HBA Port="0" WWNN="00-00-00-00-00-00-00-00" WWPN="00-00-00-00-00-00-00-00" /> 
             <Param ConnectionOption="1" 
                  DataRate="1" 
                  FrameSize="2048" 
                  HardLoopID="0" 
                  LoopResetDelay="5" 
                  EnableHostAdapterBIOS="1" 
                  EnableHardLoopId="0" 
                  FibreChannelTapeSupport="1" 
                  ExecutionThrottle="256" 
                  LoginRetryCount="8" 
                  PortDownRetryCount="45" 
                  EnableLipFulllogin="1" 
                  LinkDownTimeout="45" 
                  EnableTargetReset="1" 
                  LUNsPerTarget="256"
             />   
         </HBA>
         <HBA>
              <HBA Port="1" WWNN="00-00-00-00-00-00-00-01" WWPN="00-00-00-00-00-00-00-01" /> 
              <Param
                   ConnectionOption="1" 
                   DataRate="1" 
                   FrameSize="2048" 
                   HardLoopID="0" 
                   LoopResetDelay="5" 
                   EnableHostAdapterBIOS="1" 
                   EnableHardLoopId="0" 
                   FibreChannelTapeSupport="1" 
                   ExecutionThrottle="256" 
                   LoginRetryCount="8" 
                   PortDownRetryCount="45" 
                   EnableLipFulllogin="1" 
                   LinkDownTimeout="45" 
                   EnableTargetReset="1" 
                   LUNsPerTarget="256" 
              /> 
         </HBA>
      <Status>0</Status> 
      <Reboot>0</Reboot> 
    </QLogic>

    I am posting this so you can get an idea of how the XML works so you can see how to query it. Notice that there are two HBAs and they are in separate containers. To use these we want to turn them into PS objects.


    ¯\_(ツ)_/¯

  • Monday, January 21, 2013 7:28 PM
     
      Has Code

    Example:

    PS > $xml=[xml](cat c:\scripts\hbadat.xml)
    PS > $xml.Qlogic.HBA[0].Param|ft
    
    ConnectionOption DataRate FrameSize HardLoopID LoopResetDelay EnableHostAdapterBIOS EnableHardLoopId FibreChannelTape
                                                                                                         pport
    ---------------- -------- --------- ---------- -------------- --------------------- ---------------- ----------------
    1                1        2048      0          5              1                     0                1

    Of course it wraps on the display but you can see it is an object.  We can now export the records to a CSV filr using PowerShell or we can combine the XML into on composite object.

    The data is hierarchical so unless it is denotmalized it cannot easily be put into a CSV file.  I recommend moving it into a composite XML file and using POwerShell to create reports.

    YOu should also be aware that the data needs to be tagged with the server name before it is combined.


    ¯\_(ツ)_/¯


  • Monday, January 21, 2013 7:54 PM
     
      Has Code

    Thank you again for the input. In case you are wondering, here is what I'm running with for now.

    # Declare and populate vars
    $SrvList = "C:\batch\dan\servers_main.lst"
    $server = Get-Content $SrvList
    $exe = "C:\batch\dan\bin\psexec.exe"
    $LogLoc = "\\<serverName>\logs\HBA Info\"
    $LogFile = $LogLoc + "Execution_Results.log"
    $hbaFiles = $LogLoc + "hbaInfo_*" 
    $hbaAll = $LogLoc + "all_hba_params.txt"
    $DateAndSuch = Date
    # Get credentials of user who's context will be used to execute psexec - the PromptForCredential method calls the Get-Credential cmdlet
    $creds = $host.ui.PromptForCredential("Account Authorization", "Please enter your username and password (i.e. domain\user).", "", "NetBiosUserName")
    # Make sure we can move forward 
    if (!(Test-Path $LogLoc)) 
    {
        New-Item $LogLoc -type directory
    }
    # Initialize and create log file
    Write-Output "$DateAndSuch -- Initializing SANSurver HBA Gather script" >> $LogFile
    if (!(Test-Path $exe)) 
    {
        Write-Output "$DateAndSuch -- psexec.exe not found!" >> $LogFile
        Write-Output "----END OF EXECUTION----" >> $LogFile
        exit
    }
    if (!(Test-Path $SrvList)) 
    {
        Write-Output "$DateAndSuch -- server.lst not found!" >> $LogFile
        Write-Output "----END OF EXECUTION----" >> $LogFile
        exit
    }
    # Set up shell
    $Host.UI.RawUI.WindowTitle = "SANSurfer Gather Script"
    clear
    Write-Output "$DateAndSuch -- Initialization complete" >> $LogFile
    # Loop through server list
    foreach ($i in $server)
    {   
        # call date variable again, to get accurate timestamp for this iteration
        $DateAndSuch = Date
        Write-Output "Gathering HBA data from $i"
        Write-Output "$DateAndSuch -- HBA data from $i" >> $LogFile
        $params = '\\'+$i+' -u ' + $creds.Username + ' -p ' + $creds.GetNetworkCredential().Password + ' -d cmd.exe /c "\\<server>\C$\batch\dan\bin\scli.bat"'
        # Write-Output $params
    	$execResult = (Start-Process -FilePath $exe -ArgumentList $params -Wait -PassThru).ExitCode
        $execResult = [int]$execResult
        
        if ($execResult = '0')
        {
            Write-Host "Execution result    " -NoNewline
            Write-Host "$execResult    " -ForegroundColor Green -NoNewline
            Write-Host "on    $i"
        }
        else
        {
            Write-Host "Execution result    " -NoNewline
            Write-Host "$execResult    " -ForegroundColor Red -NoNewline
            Write-Host "on    $i"
        }
        
        Write-Output "$DateAndSuch -- Finished executing HBA data gathering on $i with this return code: $execResult" >> $LogFile
    }
    Write-Output "----END OF EXECUTION----" >> $LogFile
    # Emulate the pause command in old school batch
    Write-Output "Press any key to continue ..."
    $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") # <-- this command produces an error in ISE, but works fine when invoked by powershell.exe
    # Combine HBA files 
    [string]$outpath = $LogLoc + "all_hba_params.txt"
    Get-ChildItem -path $LogLoc -recurse |?{ ! $_.PSIsContainer } |?{($_.name).contains(".txt")} | %{ Out-File -filepath $outpath -inputobject (get-content $_.fullname) -Append}
    # Parse "all" file 
    $HBAdata=Get-Content $hbaAll
    $instancecount=(((Select-String -Inputobject $HBAdata -Pattern 'HBA Instance' -AllMatches).Matches).Count) -1
    0..$instancecount | ForEach{New-Variable -Name "hash$($_)" -value @{} -force}
    $HBAdata | ForEach{
        $tempname,$tempvalue=$_ -split '\s+:\s'
        For($i=0;$i -le $instancecount;$i++){
            if(!(iex $"hash$($i).ContainsKey('$tempname')")){iex $"hash$($i).Add('$tempname','$tempvalue')";$i=$instancecount}
        }
    }
    $output=@()
    For($i=0;$i -le $instancecount;$i++){
        iex $"output+=New-Object PSObject -Property $"hash$($i)
    }
    # output to text
    $output | ft 'Host Name','HBA Port','HBA Instance', 'Port ID', 'HBA Port', 'Port Name', 'HBA Model', 'HBA Status', 'Connection Options', 'Actual Connection Mode', 'Data Rate', 'Actual Data Rate', 'Execution Throttle', 'Port Down Retry Count', 'Link Down Timeout (seconds)' -Wrap > "C:\batch\dan\HBA_All.txt"
    # output to grid
    $output | Out-GridView
    # output to csv
    $output | Export-Csv -path "C:\batch\dan\HBA_All.csv"


    • Edited by Dan Hyman Monday, January 21, 2013 7:56 PM
    •  
  • Monday, January 21, 2013 7:59 PM
     
     

    The XML data can also be directly opened or imported into Excel without using PowerShell.  It is structured correctly for use in Excel.


    ¯\_(ツ)_/¯

  • Monday, January 21, 2013 9:04 PM
     
      Has Code

    Thank you again for the input. In case you are wondering, here is what I'm running with for now.

    # Declare and populate vars
    $SrvList = "C:\batch\dan\servers_main.lst"
    $server = Get-Content $SrvList
    $exe = "C:\batch\dan\bin\psexec.exe"
    $LogLoc = "\\<serverName>\logs\HBA Info\"
    $LogFile = $LogLoc + "Execution_Results.log"
    $hbaFiles = $LogLoc + "hbaInfo_*" 
    $hbaAll = $LogLoc + "all_hba_params.txt"
    $DateAndSuch = Date
    # Get credentials of user who's context will be used to execute psexec - the PromptForCredential method calls the Get-Credential cmdlet
    $creds = $host.ui.PromptForCredential("Account Authorization", "Please enter your username and password (i.e. domain\user).", "", "NetBiosUserName")
    # Make sure we can move forward 
    if (!(Test-Path $LogLoc)) 
    {
        New-Item $LogLoc -type directory
    }
    # Initialize and create log file
    Write-Output "$DateAndSuch -- Initializing SANSurver HBA Gather script" >> $LogFile
    if (!(Test-Path $exe)) 
    {
        Write-Output "$DateAndSuch -- psexec.exe not found!" >> $LogFile
        Write-Output "----END OF EXECUTION----" >> $LogFile
        exit
    }
    if (!(Test-Path $SrvList)) 
    {
        Write-Output "$DateAndSuch -- server.lst not found!" >> $LogFile
        Write-Output "----END OF EXECUTION----" >> $LogFile
        exit
    }
    # Set up shell
    $Host.UI.RawUI.WindowTitle = "SANSurfer Gather Script"
    clear
    Write-Output "$DateAndSuch -- Initialization complete" >> $LogFile
    # Loop through server list
    foreach ($i in $server)
    {   
        # call date variable again, to get accurate timestamp for this iteration
        $DateAndSuch = Date
        Write-Output "Gathering HBA data from $i"
        Write-Output "$DateAndSuch -- HBA data from $i" >> $LogFile
        $params = '\\'+$i+' -u ' + $creds.Username + ' -p ' + $creds.GetNetworkCredential().Password + ' -d cmd.exe /c "\\<server>\C$\batch\dan\bin\scli.bat"'
        # Write-Output $params
    	$execResult = (Start-Process -FilePath $exe -ArgumentList $params -Wait -PassThru).ExitCode
        $execResult = [int]$execResult
        
        if ($execResult = '0')
        {
            Write-Host "Execution result    " -NoNewline
            Write-Host "$execResult    " -ForegroundColor Green -NoNewline
            Write-Host "on    $i"
        }
        else
        {
            Write-Host "Execution result    " -NoNewline
            Write-Host "$execResult    " -ForegroundColor Red -NoNewline
            Write-Host "on    $i"
        }
        
        Write-Output "$DateAndSuch -- Finished executing HBA data gathering on $i with this return code: $execResult" >> $LogFile
    }
    Write-Output "----END OF EXECUTION----" >> $LogFile
    # Emulate the pause command in old school batch
    Write-Output "Press any key to continue ..."
    $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") # <-- this command produces an error in ISE, but works fine when invoked by powershell.exe
    # Combine HBA files 
    [string]$outpath = $LogLoc + "all_hba_params.txt"
    Get-ChildItem -path $LogLoc -recurse |?{ ! $_.PSIsContainer } |?{($_.name).contains(".txt")} | %{ Out-File -filepath $outpath -inputobject (get-content $_.fullname) -Append}
    # Parse "all" file 
    $HBAdata=Get-Content $hbaAll
    $instancecount=(((Select-String -Inputobject $HBAdata -Pattern 'HBA Instance' -AllMatches).Matches).Count) -1
    0..$instancecount | ForEach{New-Variable -Name "hash$($_)" -value @{} -force}
    $HBAdata | ForEach{
        $tempname,$tempvalue=$_ -split '\s+:\s'
        For($i=0;$i -le $instancecount;$i++){
            if(!(iex $"hash$($i).ContainsKey('$tempname')")){iex $"hash$($i).Add('$tempname','$tempvalue')";$i=$instancecount}
        }
    }
    $output=@()
    For($i=0;$i -le $instancecount;$i++){
        iex $"output+=New-Object PSObject -Property $"hash$($i)
    }
    # output to text
    $output | ft 'Host Name','HBA Port','HBA Instance', 'Port ID', 'HBA Port', 'Port Name', 'HBA Model', 'HBA Status', 'Connection Options', 'Actual Connection Mode', 'Data Rate', 'Actual Data Rate', 'Execution Throttle', 'Port Down Retry Count', 'Link Down Timeout (seconds)' -Wrap > "C:\batch\dan\HBA_All.txt"
    # output to grid
    $output | Out-GridView
    # output to csv
    $output | Export-Csv -path "C:\batch\dan\HBA_All.csv"



    If it does what you want then use it,  It is about 10 times as many lines of code as would be necessary using XML.  Next time always look at the XML solution early as much of the time it will reduce the problem size.

    ¯\_(ツ)_/¯