ค้นหาเว็บไซต์

สนุกยิ่งขึ้นด้วยคุณสมบัติของสตริง


วันก่อนฉันโพสต์บทความเกี่ยวกับการแปลงคุณสมบัติสตริงที่คุณอาจได้รับจากการเรียกใช้เครื่องมือบรรทัดคำสั่งเป็นคุณสมบัติที่มีชื่อ PowerShell ฉันยังคงทดลองกับมันต่อไป นี่คือโค้ดบางส่วนเกี่ยวกับวิธีการใช้งานของฉัน

$raw = qprocess
$properties = $raw[0] -split "\s{2,}" | Convert-StringProperty 
$raw | select -Skip 1 | foreach {
 #split each line
 $data = $_ -split "\s{2,}"
 $hash=[ordered]@{}
 for ($i=0;$i -lt $properties.count;$i++) {
   $hash.Add($properties[$i],$data[$i])
 }
 [pscustomobject]$hash
}

ฉันลงเอยด้วยผลลัพธ์เช่นนี้:

Username    : >jeff
Sessionname : console
Id          : 1
Pid         : 3044
Image       : conhost.exe

Username    : >jeff
Sessionname : console
Id          : 1
Pid         : 4848
Image       : chrome.exe

เป็นการดีที่จะล้างค่าและลบอักขระที่ไม่ใช่คำเช่น “<“ แต่ฉันไม่ต้องการลบช่วงเวลาออกจากคุณสมบัติรูปภาพ ดังนั้นฉันจึงคิดสิ่งนี้ขึ้นมา:

$raw = qprocess
$properties = $raw[0] -split "\s{2,}" | Convert-StringProperty 
$raw | select -Skip 1 | foreach {
 #split each line
 $data = $_ -split "\s{2,}"
 $hash=[ordered]@{}
 for ($i=0;$i -lt $properties.count;$i++) {
   #remove any < > or # characters from data value
   $hash.Add($properties[$i],($data[$i] -replace "[><#]",""))
 }
 [pscustomobject]$hash
}

แต่ละองค์ประกอบข้อมูลจะถูกล้างข้อมูลโดยใช้ตัวดำเนินการแทนที่เพื่อแทนที่อักขระใดๆ ในรูปแบบนิพจน์ทั่วไปโดยไม่มีสิ่งใดเลย จากนั้น ฉันเริ่มทดลองใช้เอาต์พุตบรรทัดคำสั่งที่แสดงเป็นชุดบรรทัด เช่น ipconfig /displaydns ก่อนอื่น ฉันต้องตัดสินใจว่าจะดำเนินการใด ดังนั้นฉันต้องการได้บรรทัดทั้งหมดที่มี ' . .' ในนั้น.

$raw = ipconfig /displaydns | where {$_ -match "\.\s"}

เมื่อดูที่ผลลัพธ์ ฉันพบว่าโดยพื้นฐานแล้วฉันได้รับ "วัตถุ" สำหรับทุก ๆ กลุ่มของ 5 บรรทัด

$group = 5
for ($i=0;$i -lt $raw.count;$i+=($group+1)) {
   $raw[$i..($i+$group)] | foreach -begin {
  $hash=[ordered]@{}
  } -process {
    #split on the first : into 2 strings
    $split = $_ -split ":",2
    $prop = $split[0] | Convert-StringProperty
    $value = $split[1]
    $hash.add($prop,$value)
  } -end {
     [pscustomobject]$hash
  }
}

รหัสนี้นับเป็นกลุ่ม 5 และรับองค์ประกอบแต่ละกลุ่มโดยใช้ช่วงของหมายเลขดัชนี จากนั้นแต่ละบรรทัดในช่วงจะแบ่งออกเป็น 2 สายที่อักขระตัวแรก : รายการแรกจะเป็นชื่อคุณสมบัติซึ่งจะถูกแปลงเป็นคุณสมบัติสตริง และรายการที่สองจะเป็นข้อมูล สิ่งนี้ทำให้ฉันมีวัตถุที่กำหนดเอง:

RecordName  :  ec2-50-16-209-174.compute-1.amazonaws.com
RecordType  :  1
TimeToLive  :  19610
DataLength  :  4
Section     :  Answer
AhostRecord :  50.16.209.174

RecordName :  13.30.16.172.in-addr.arpa.
RecordType :  12
TimeToLive :  86400
DataLength :  8
Section    :  Answer
PtrRecord  :  chi-core01.globomantics.local

ซึ่งนำไปสู่ความท้าทายต่อไป บรรทัดสุดท้ายถูกตีความว่าเป็นคุณสมบัติสองประการที่แยกจากกันซึ่งค่อนข้างยุ่งเหยิงเล็กน้อย ดังนั้นฉันจึงต้องการวิธีเปลี่ยนชื่อบรรทัดนั้น หรือจริงๆ แล้วเป็นแนวไหนก็ได้ บางทีแทนที่จะเป็น TimeToLive ฉันต้องการใช้ TTL นี่คือขั้นตอนต่อไป

$replaceHash=@{5="Record";2="TTL"}

$raw = ipconfig /displaydns | where {$_ -match "\.\s"}
$group = 5

for ($i=0;$i -lt $raw.count;$i+=($group+1)) {
   $raw[$i..($i+$group)] | foreach -begin {
     $hash=[ordered]@{}
     #define an process counter
     $k=0
  } -process {
    $split = $_ -split ":",2
    if ($replaceHash.keys -contains $k) {
      #get replacement property name from hash table
      $prop = $replaceHash.Item($k)
    }
    else {
        #convert existing property name
        $prop = $split[0] | Convert-StringProperty
    }
    $value = $split[1]
    $hash.add($prop,$value)
    #increment the property counter
    $k++
  } -end {
     [pscustomobject]$hash
  }
}

ฉันต้องทราบล่วงหน้าว่าทรัพย์สินแต่ละแห่งจะอยู่บนหมายเลขบรรทัดใด แต่ตั้งแต่ฉันทำ ฉันจะสร้างแฮชเทเบิลที่ใช้หมายเลขบรรทัด (เริ่มต้นที่ 0) เป็นคีย์ ค่าแฮชจะเป็นชื่อคุณสมบัติทดแทน ในกระบวนการ scriptblock ของฉัน ฉันกำลังติดตามว่าฉันได้ทำงานไปกี่บรรทัดแล้ว หากตัวนับตรงกับคีย์ในแฮชเทเบิล ฉันจะทำการแทนที่ ตอนนี้ฉันได้รับสิ่งนี้:

RecordName :  ec2-50-16-209-174.compute-1.amazonaws.com
RecordType :  1
TTL        :  19044
DataLength :  4
Section    :  Answer
Record     :  50.16.209.174

RecordName :  13.30.16.172.in-addr.arpa.
RecordType :  12
TTL        :  86400
DataLength :  8
Section    :  Answer
Record     :  chi-core01.globomantics.local

ณ จุดนี้ ฉันรับรู้ว่าฉันมีโค้ดที่ทำซ้ำได้อีกตัวหนึ่งซึ่งสามารถเปลี่ยนให้เป็นฟังก์ชันได้

Function Convert-CLIOutput {

<#
.SYNOPSIS
Convert CLI-based tool output to PowerShell object
.DESCRIPTION
This command will process output from a command line tool and turn it into
PowerShell pipeline output. It relies on the Convert-StringProperty function.
This command works best with tabular output where column headings are separated
by at least 2 spaces.

You can use list oriented data but you must tell the command you are processing
a list as well as tell it how many lines are grouped together to form a new
object.

By default, the command will remove any non-word characters from the data such as
> or # using a regular expression pattern for -Cleanup. Use -NoCleanup if you want
the data as it appears in the command line tool output. NoCleanup will take
precedence over Cleanup.

Sometimes properties aren't what you want so you can use a custom hashtable
to specify replacement values. The key will the number of the property starting
at 1. In a table count left to right. In a list count top down. The value is 
the replacement property value.

You cannot pipe anything into this command.
.PARAMETER Delimiter
The delimiter for property names in the CLI output. The default is a space.
This command uses Convert-StringProperty to convert a string like 
"Internet address" into a "InternetAddress"
.PARAMETER ReplaceHash
A hashtable of replacement property values.  The key will the number of the 
property starting at 1. In a table count left to right. In a list count top 
down. The value is the replacement property value. See examples.
.PARAMETER List
Specify that the input is formatted as a list
.PARAMETER Group
The number of lines that will be grouped together to define a single set of 
.PARAMETER LineDelimiter
When processing line output, this will specify the delimiter separating the
property from the value. The default is the colon.
object properties.
.PARAMETER Cleanup
A regular expression pattern of characters to clean up from data output. The
default is [<>$#].
.EXAMPLE
PS C:\> $raw = netstat -n | select -skip 3
PS C:\> convert-clioutput $raw | where {$_.state -match "Time_Wait"}

Proto              LocalAddress                     ForeignAddress                   State                           
-----              ------------                     --------------                   -----                           
TCP                172.16.10.127:50115              172.16.10.86:80                 TIME_WAIT 

.EXAMPLE
PS C:\> $raw = ipconfig /displaydns | where {$_ -match "\.\s"}
PS C:\> Convert-CLIOutput $raw -list -group 6 -ReplaceHash @{6="Record"} -NoCleanup

RecordName :  ec2-54-221-234-232.compute-1.amazonaws.com
RecordType :  1
TimeToLive :  1277
DataLength :  4
Section    :  Answer
Record     :  54.221.234.232

RecordName :  13.30.16.172.in-addr.arpa.
RecordType :  12
TimeToLive :  86400
DataLength :  8
Section    :  Answer
Record     :  chi-core01.globomantics.local
...

The output from Ipconfig /displaydns is presented in a list. In this example, the 
output for each entry is grouped in 6 lines. The replacement hash table replaces
the property for the 6th line with a different value. The expression is using the
-NoCleanup parameter so that record names are not stripped of periods and dashes.
.EXAMPLE
PS C:\> Convert-CLIOutput (quser) -ReplaceHash @{1="User";2="Session"} 

User      : jeff
Session   : console
Id        : 1
State     : Active
IdleTime  : none
LogonTime : 3262014345PM

This command takes table output from quser.exe and turns it into an object using
a hashtable of replacement values.
#>

[cmdletbinding(DefaultParameterSetName="Table")]
Param(
[Parameter(Position=0)]
$InputObject,
[string]$Delimiter=" ",
[switch]$NoCleanup,
[hashtable]$ReplaceHash,
[regex]$Cleanup="[<>#?]",
[Parameter(ParameterSetName="List")]
[switch]$List,
[Parameter(ParameterSetName="List",Mandatory=$True,
HelpMessage="How many lines in a group?")]
[int]$Group,
[Parameter(ParameterSetName="List")]
[ValidateNotNullorEmpty()]
[string]$LineDelimiter=":"
)

Begin {
    Write-Verbose "Starting $($MyInvocation.Mycommand)"
    Write-Verbose "Using parameter set $($PSCmdlet.ParameterSetName)"
    [regex]$rx="\s{2,}"
} #Begin
Process {

    If ($PSCmdlet.ParameterSetName -eq "Table") {

        Write-Verbose "Processing property names"
        $properties = $rx.Split($InputObject[0].trim()) | Convert-StringProperty -Delimiter $delimiter

        Write-Verbose "Converting data to objects"
        for ($i=1;$i -lt $inputObject.count; $i++) {
          $splitData = $rx.split($inputobject[$i].Trim())
          #create an object for each entry
          $hash = [ordered]@{}
          for ($j=0;$j -lt $properties.count;$j++) {
             if ($NoCleanup) {
                 $value = $splitData[$j]
             }
             else {
                #add each data element stripped of any non word characters like < or #
                $value = $splitData[$j] -replace $Cleanup,''
            }

            #replace properties with values from hashtable
            if ($ReplaceHash.keys -contains ($j+1)) {

               $newProperty = $ReplaceHash.Item($j+1)
               Write-verbose "Using replacement property $newproperty"
            }
            else {
               $newProperty = $properties[$j]
            }
            #add the property key and value to the hashtable
            $hash.Add($newProperty,$value)
          } #for
            #Write a custom object to the pipeline
            [pscustomobject]$hash
        } #for
    } #if table
    else {
      for ($i=0;$i -lt $inputobject.count;$i+=$group) {
       $inputobject[$i..($i+($group-1))] | foreach -begin {
         $hash=[ordered]@{}
         #define an process counter
         $k=1
      } -process {
        #split the line on the first line delimiter.
        $split = $_ -split $LineDelimiter,2
        if ($replaceHash.keys -contains $k) {
          #get replacement property name from hash table if processing the 
          #matching line number
          $prop = $replaceHash.Item($k)
        }
        else {
            #convert existing property name
            $prop = $split[0] | Convert-StringProperty -Delimiter $delimiter
        }
        if ($NoCleanup) {
            $value = $split[1]
        }
        else {
              #add each data element stripped of any non word characters like < or #
              $value = $split[1] -replace $Cleanup,''
         }

        $hash.add($prop,$value)
        #increment the property counter
        $k++
       } #process
        #Write a custom object to the pipeline
        [pscustomobject]$hash
    } #for
    } #else   

} #process

End { 
    Write-Verbose -Message "Ending $($MyInvocation.Mycommand)"
}
} #end function

ฟังก์ชันนี้อาศัย Convert-StringProperty เมื่อถึงจุดหนึ่ง ฉันควรรวมสิ่งนี้และสิ่งอื่นๆ ที่ฉันมีไว้ในโมดูล แต่สำหรับตอนนี้ คุณจะต้องแน่ใจว่ามีการโหลดฟังก์ชันอื่นลงในเชลล์ของคุณแล้ว การประมวลผลเริ่มต้นจะถือว่าคุณมีเอาต์พุตแบบตารางเหมือนกับที่คุณได้รับจาก arp -a คุณสามารถระบุอินพุตที่อยู่ในรายการได้ แต่คุณต้องระบุจำนวนบรรทัดที่จะจัดกลุ่มไว้ด้วยกันด้วย คุณยังสามารถระบุ hashtable ของชื่อคุณสมบัติการแทนที่สำหรับประเภทอินพุตใดก็ได้ เพื่อให้สิ่งต่าง ๆ ง่ายขึ้น คีย์แฮชจะเริ่มนับที่ 1

คุณไม่สามารถไปป์คำสั่งได้ คุณต้องระบุอินพุตดังนี้:

ต่อไปนี้เป็นตัวอย่างอื่นๆ ที่ใช้เอาต์พุตแบบตารางรวมถึงการเปลี่ยนชื่อคุณสมบัติ

จากตัวอย่าง IPCONFIG ของฉันจากก่อนหน้านี้ ฉันสามารถทำได้ด้วยฟังก์ชันใหม่ของฉัน

$raw = ipconfig /displaydns | where {$_ -match "\.\s"}
Convert-CLIOutput $raw -ReplaceHash @{3="TTL";6="Record"} -list -group 6 | Out-GridView -Title DisplayDNS

ในมุมมองตาราง ฉันสามารถกรองหรือจัดเรียงเพิ่มเติมได้

ปัญหาหนึ่งที่เหลืออยู่ที่นี่คือทุกอย่างเป็นสตริง แต่ในแง่นั้น มันก็ไม่ได้แตกต่างไปจากเมื่อคุณนำเข้าไฟล์ CSV มากนัก ฉันเดาว่านี่ทำให้ฉันมีอย่างอื่นให้ทำงานต่อไป ในระหว่างนี้ ลองใช้วิธีนี้แล้วแจ้งให้เราทราบว่าคุณคิดอย่างไร

บทความที่เกี่ยวข้อง