code

PowerShell에서 어레이의 모든 개체에서 하나의 속성 값 선택

starcafe 2023. 4. 8. 09:04
반응형

PowerShell에서 어레이의 모든 개체에서 하나의 속성 값 선택

오브젝트 $objects 배열이 있다고 칩시다.이러한 개체에 "이름" 속성이 있다고 가정합니다.

이것이 내가 하고 싶은 것이다.

 $results = @()
 $objects | %{ $results += $_.Name }

이것은 효과가 있지만, 더 나은 방법으로 할 수 있을까요?

다음과 같은 작업을 수행할 경우:

 $results = objects | select Name

$results이름 지어주세요.$results name이름 。

더 좋은 방법이 있을까요?

내 생각엔 네가 이 모든 것들을ExpandProperty「」Select-Object.

예를 들어 현재 디렉토리의 목록을 가져오고 이름 속성만 표시하려면 다음을 수행합니다.

ls | select -Property Name

아직 디렉토리를 반환하고 있습니다.Info 또는 FileInfo 객체.Get-Member(일명)에 파이프로 연결하여 항상 파이프라인을 통과하는 유형을 검사할 수 있습니다.gm를 참조해 주세요.

ls | select -Property Name | gm

따라서 개체를 현재 속성 유형으로 확장하려면 다음을 수행합니다.

ls | select -ExpandProperty Name

이 경우 다음 작업을 수행하여 변수를 문자열 배열로 지정할 수 있습니다. 여기서 string은 Name 속성입니다.

$objects = ls | select -ExpandProperty Name

보다 쉬운 솔루션으로서 다음과 같은 것을 사용할 수 있습니다.

$results = $objects.Name

요?$resultsName"에 " 값 $objects.

기존의 유용한 답변을 보완하기 위해 어떤 접근방식을 사용해야 할지에 대한 지침과 성능 비교를 제공합니다.

  • 파이프라인 [1] 에서 다음을 사용합니다(PSv3+ 필요).

    $objects.Name # returns .Name property values from all objects in $objects
    

    againandq의 답변에서 알 수 있듯이, 구문적으로 더 간단하고 훨씬 더 빠릅니다.

    • 컬렉션 수준에서 속성에 액세스하여 요소의 값을 배열로 가져오는 것을 멤버 액세스 열거라고 하며 PSv3+ 기능입니다.

    • 또는 PSv2에서는foreach 출력은 변수에 직접 할당할 수도 있습니다.

      $140 = foreach ($obj in $140) { $obj.이름 }

    • (파이프라인) 명령어로부터의 모든 출력을 메모리로 수집할 수 있는 경우 파이프라인을 멤버액세스 열거와 조합있습니다.예를 들어 다음과 같습니다.

       (Get-ChildItem -File | Where-Object Length -lt 1gb).Name
      
    • 트레이드오프:

      • 입력 수집과 출력 배열은 모두 메모리에 전체적으로 맞아야 합니다.
      • 라인를 들어, 「」( 「파이프 라인」)는 다음과 같습니다.(Get-ChildItem).Name 명령어를 실행하여 완료해야 어레이 요소에 액세스할 수 있습니다.
  • 파이프라인에서 결과를 다른 명령어로 전달해야 하는 경우, 특히 원래 입력이 전체적으로 메모리에 맞지 않는 경우 다음을 사용합니다.

    $objects | Select-Object - ExpandProperty 이름

    • 「 」의 -ExpandPropertyScott Saad의 답변에 설명되어 있습니다(프로퍼티 값만 얻으려면 필요합니다).
    • 파이프라인의 스트리밍 동작에 의해 통상적인 파이프라인의 이점을 얻을 수 있습니다.즉, 1개씩의 오브젝트 처리에서는 일반적으로 즉시 출력이 생성되어 메모리 사용량이 일정하게 유지됩니다(최종적으로 결과를 메모리에 수집하지 않는 한).
    • 트레이드오프:
      • 파이프라인의 사용은 비교적 느리다.

작은 입력 수집(어레이)경우 차이를 알아차리지 못할 수 있으며, 특히 명령줄에서 명령어를 쉽게 입력할 수 있는 것이 더 중요합니다.


여기 입력하기 쉬운 대안이 있습니다. 단, 가장 느린 접근법입니다.ForEach-Object에일리어스 「」를 통해%구문을 단순화(다시 말하면 PSv3+): 예를 들어 다음과 같은 PSv3+ 솔루션을 기존 명령어에 쉽게 추가할 수 있습니다.

$objects | % Name      # short for: $objects | ForEach-Object -Process { $_.Name }

참고: 이 접근 방식이 느린 주된 이유는 파이프라인 사용이 아닙니다. cmdlet(및 )의 비효율적인 구현이 PowerShell 7.2 이상에 해당하기 때문입니다.이 훌륭한 블로그 투고는 문제를 설명하고 있습니다.기능 요청 GitHub 문제 #10982로 이어졌습니다.다음의 회피책은 동작 속도를 크게 높입니다(이것보다 약간 느립니다).foreach 더 빠르다..ForEach()

# Speed-optimized version of the above.
# (Use `&` instead of `.` to run in a child scope)
$objects | . { process { $_.Name } }

PSv4+ 어레이 방식(이 문서에서 보다 포괄적으로 설명)은 또 하나의 고성능 대안이지만 멤버 액세스 열거와 마찬가지로 먼저 모든 입력을 메모리에 수집해야 합니다.

# By property name (string):
$objects.ForEach('Name')

# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
  • 이 접근법은 파이프라인 로직이 적용되지 않는다는 점을 제외하고 동일한 트레이드오프를 갖는 멤버액세스 열거와 비슷합니다.멤버 액세스 열거보다 약간 느리지만 파이프라인보다 현저하게 빠릅니다.

  • 이름(string 인수)으로 단일 속성 값을 추출할 경우 이 솔루션은 member-access enumeration과 동등합니다(단, member-access enumeration은 구문적으로 단순합니다).

  • 스크립트 블록바리안트(){ ... }는 임의의 변환 가능하게 합니다.이는 파이프라인 기반의 cmdlet()%을 대체하는 고속 올인 메모리입니다.

★★★★★★.ForEach()array 메서드는 형제(메모리 내 동등한 것)와 마찬가지로 출력 객체가 하나생성되는 경우에도 항상 컬렉션(의 인스턴스)을 반환합니다.
은 " " " 입니다.Select-Object,ForEach-Object ★★★★★★★★★★★★★★★★★」Where-Object단일 출력 개체를 컬렉션(배열)으로 래핑하지 않고 그대로 반환합니다.


다양한 접근방식의 퍼포먼스 비교

다음은 10회의 실행에서 평균화된 객체 입력 컬렉션을 기반으로 한 다양한 접근 방식의 타이밍 입니다. 절대 수치는 중요하지 않고 여러 요소에 따라 다르지만 상대적인 성능을 느낄 수 있습니다(이 타이밍은 단일 코어 Windows 10 VM에서 가져옵니다).

중요한

  • 상대적인 퍼포먼스는 입력 객체가 일반 인스턴스인지에 따라 달라집니다.NET 타입(출력 등)Get-ChildItem또는 인스턴스(예: 출력)Convert-FromCsv를 참조해 주세요.
    는 ★★★★★★★★★★★★★★★★★★★.[pscustomobject]속성은 PowerShell에 의해 동적으로 관리되며 (정적으로 정의된) 일반 속성보다 빠르게 액세스할 수 있습니다.NET both both are。두 모두 되어 있습니다.두 시나리오 모두 아래에 설명되어 있습니다.

  • 이 테스트에서는 순수 속성 추출 성능에 초점을 맞추기 위해 이미 인 메모리 풀 컬렉션을 입력으로 사용합니다.스트리밍 cmdlet/함수 호출을 입력으로 사용하면 일반적으로 성능 차이는 훨씬 덜합니다. 왜냐하면 해당 호출에 소요되는 시간이 대부분의 시간을 차지하기 때문입니다.

  • 하기 에일리어스 「」를 지정합니다.%요.ForEach-Objectsysloglet.

일반적인 결론, 양쪽 모두에 적용됩니다.NET 타입과[pscustomobject] 표시:

  • 멤버 등록()$collection.Name 솔루션은 가장 빠른 파이프라인 기반 솔루션보다 10배 이상 빠릅니다.

  • ★★★★★★★★★★★★★★.% Name 나쁘다% { $_.Name }- 이 GitHub 문제를 참조하십시오.

  • PowerShell Core는 일관되게 Windows Powershell을 능가합니다.

표준에서의 타이밍NET 유형:

  • PowerShell Core v7.0.0-preview.3
Factor Command                                       Secs (10-run avg.)
------ -------                                       ------------------
1.00   $objects.Name                                 0.005
1.06   foreach($o in $objects) { $o.Name }           0.005
6.25   $objects.ForEach('Name')                      0.028
10.22  $objects.ForEach({ $_.Name })                 0.046
17.52  $objects | % { $_.Name }                      0.079
30.97  $objects | Select-Object -ExpandProperty Name 0.140
32.76  $objects | % Name                             0.148
  • Windows PowerShell v5.1.18362.145
Factor Command                                       Secs (10-run avg.)
------ -------                                       ------------------
1.00   $objects.Name                                 0.012
1.32   foreach($o in $objects) { $o.Name }           0.015
9.07   $objects.ForEach({ $_.Name })                 0.105
10.30  $objects.ForEach('Name')                      0.119
12.70  $objects | % { $_.Name }                      0.147
27.04  $objects | % Name                             0.312
29.70  $objects | Select-Object -ExpandProperty Name 0.343

결론:

  • PowerShell Core에서는.ForEach('Name') 능가하다.ForEach({ $_.Name })Windows PowerShell windows windows windows 。

인스턴스에서의 타이밍:

  • PowerShell Core v7.0.0-preview.3
Factor Command                                       Secs (10-run avg.)
------ -------                                       ------------------
1.00   $objects.Name                                 0.006
1.11   foreach($o in $objects) { $o.Name }           0.007
1.52   $objects.ForEach('Name')                      0.009
6.11   $objects.ForEach({ $_.Name })                 0.038
9.47   $objects | Select-Object -ExpandProperty Name 0.058
10.29  $objects | % { $_.Name }                      0.063
29.77  $objects | % Name                             0.184
  • Windows PowerShell v5.1.18362.145
Factor Command                                       Secs (10-run avg.)
------ -------                                       ------------------
1.00   $objects.Name                                 0.008
1.14   foreach($o in $objects) { $o.Name }           0.009
1.76   $objects.ForEach('Name')                      0.015
10.36  $objects | Select-Object -ExpandProperty Name 0.085
11.18  $objects.ForEach({ $_.Name })                 0.092
16.79  $objects | % { $_.Name }                      0.138
61.14  $objects | % Name                             0.503

결론:

  • 를 사용하는 방법에 해 주세요.[pscustomobject] ★★.ForEach('Name')스크립트 블록 기반의 변종보다 훨씬 뛰어난 성능을 발휘합니다..ForEach({ $_.Name }).

  • similarly유 similarly,, similarly 、[pscustomobject]에 의해 베이스의 「」가 .Select-Object -ExpandProperty Name PowerShell과 성능을 합니다..ForEach({ $_.Name })PowerShell Core의 50%를 사용합니다.

  • 요컨대, 다음과 같은 이상한 예외는 제외하고% Name, 를 사용하여, 를 참조해 주세요.[pscustomobject]속성을 참조하는 문자열 기반 메서드는 스크립트 블록 기반 메서드보다 성능이 우수합니다.


테스트 소스 코드:

주의:

  • 기능Time-Command이 테스트를 실행할 수 있습니다.

    • 링크된 코드가 안전한지 확인했다고 가정하면(개인적으로 확인할 수 있지만 항상 확인해야 합니다), 다음과 같이 직접 설치할 수 있습니다.

      irm https://gist.github.com/mklement0/9e1f13978620b09ab2d15da5535d1b27/raw/Time-Command.ps1 | iex
      
  • ★★$useCustomObjectInput로로 합니다.$true[pscustomobject]대신 인스턴스(instance)를 사용합니다.

$count = 1e4 # max. input object count == 10,000
$runs  = 10  # number of runs to average 

# Note: Using [pscustomobject] instances rather than instances of 
#       regular .NET types changes the performance characteristics.
# Set this to $true to test with [pscustomobject] instances below.
$useCustomObjectInput = $false

# Create sample input objects.
if ($useCustomObjectInput) {
  # Use [pscustomobject] instances.
  $objects = 1..$count | % { [pscustomobject] @{ Name = "$foobar_$_"; Other1 = 1; Other2 = 2; Other3 = 3; Other4 = 4 } }
} else {
  # Use instances of a regular .NET type.
  # Note: The actual count of files and folders in your file-system
  #       may be less than $count
  $objects = Get-ChildItem / -Recurse -ErrorAction Ignore | Select-Object -First $count
}

Write-Host "Comparing property-value extraction methods with $($objects.Count) input objects, averaged over $runs runs..."

# An array of script blocks with the various approaches.
$approaches = { $objects | Select-Object -ExpandProperty Name },
              { $objects | % Name },
              { $objects | % { $_.Name } },
              { $objects.ForEach('Name') },
              { $objects.ForEach({ $_.Name }) },
              { $objects.Name },
              { foreach($o in $objects) { $o.Name } }

# Time the approaches and sort them by execution time (fastest first):
Time-Command $approaches -Count $runs | Select Factor, Command, Secs*

으로는 [1]이 없는 |파이프라인 운영자는 백그라운드에서 파이프라인을 사용하지만 이 설명의 목적상 파이프라인을 사용하는 것은 다음 명령어를 사용하는 것 뿐입니다.|파이프라인 연산자, 정의상 여러 명령어가 필요합니다.

주의: 멤버 열거는 컬렉션 자체에 같은 이름의 멤버가 없는 경우에만 작동합니다.따라서 FileInfo 객체의 배열이 있는 경우 파일 길이의 배열은 다음 방법으로 얻을 수 없습니다.

 $files.length # evaluates to array length

그리고 "당연히"라고 말하기 전에, 이것을 고려하세요.용량 속성을 가진 개체 배열이 있는 경우

 $objarr.capacity

$objarr이 실제로 [Array]가 아닌 [ArrayList]가 아니라면 정상적으로 동작합니다.따라서 멤버 열거를 사용하기 전에 컬렉션이 들어 있는 블랙박스 내부를 확인해야 할 수도 있습니다.

(모델레이터 주의: 이것은 raigandq의 답변에 대한 코멘트가 되어야 하지만, 저는 아직 충분한 평판을 얻지 못했습니다.)

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★! 그렇게요.★★★★★★★★★★★★★★★★★★★★★★★★★★★★★가 직접 한 예요. '이렇게 하다'$ListOfGGUIDs = $objects.{Object GUID}먼저 배열가 있다는 것을 .$ListOfGGUIDs = @()

언급URL : https://stackoverflow.com/questions/5176815/select-the-values-of-one-property-on-all-objects-of-an-array-in-powershell

반응형