Để bổ sung cho các câu trả lời có sẵn, các câu trả lời hữu ích với hướng dẫn khi nào nên sử dụng phương pháp nào và so sánh hiệu suất .
Bên ngoài đường ống, sử dụng (PSv3 +):
$ đối tượng . Tên
như thể hiện trong câu trả lời của rageandqq , cả hai đều đơn giản hơn về mặt cú pháp và nhanh hơn nhiều .
Trong một đường ống mà kết quả phải được xử lý thêm hoặc kết quả không phù hợp với toàn bộ bộ nhớ, hãy sử dụng:
$ đối tượng | Chọn-Object -ExpandProperty Name
- Sự cần thiết
-ExpandProperty
được giải thích trong câu trả lời của Scott Saad .
- Bạn nhận được các lợi ích đường ống thông thường của xử lý từng cái một, thường tạo ra đầu ra ngay lập tức và giữ cho bộ nhớ sử dụng không đổi (trừ khi cuối cùng bạn vẫn thu thập kết quả trong bộ nhớ).
- Đánh đổi :
- Sử dụng các đường ống tương đối chậm .
Đối với các bộ sưu tập đầu vào nhỏ (mảng), có lẽ bạn sẽ không nhận thấy sự khác biệt và đặc biệt là trên dòng lệnh, đôi khi việc có thể gõ lệnh dễ dàng là quan trọng hơn.
Đây là một cách thay thế dễ gõ , tuy nhiên, đây là cách tiếp cận chậm nhất ; nó sử dụng cú pháp đơn giản hóa ForEach-Object
được gọi là một câu lệnh hoạt động (một lần nữa, PSv3 +) :; ví dụ: giải pháp PSv3 + sau đây dễ dàng nối vào lệnh hiện có:
$objects | % Name # short for: $objects | ForEach-Object -Process { $_.Name }
Vì mục đích hoàn chỉnh: Phương pháp mảng PSv4 +.ForEach()
ít được biết đến , được hiểu nhiều hơn được thảo luận trong bài viết này , vẫn là một cách khác :
# By property name (string):
$objects.ForEach('Name')
# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
Cách tiếp cận này là tương tự như liệt kê thành viên , với sự cân bằng tương tự, ngoại trừ rằng logic đường ống được không áp dụng; nó chậm hơn một chút , mặc dù vẫn nhanh hơn đáng kể so với đường ống.
Để trích xuất một giá trị thuộc tính duy nhất theo tên ( đối số chuỗi ), giải pháp này ngang bằng với liệt kê thành viên (mặc dù sau này đơn giản hơn về mặt cú pháp).
Các kịch bản-block biến , cho phép tùy biến đổi ; nó là một sự thay thế nhanh hơn - tất cả trong bộ nhớ cùng một lúc - thay thế cho ForEach-Object
lệnh ghép ngắn dựa trên đường ống ( %
) .
So sánh hiệu suất của các phương pháp khác nhau
Dưới đây là thời gian mẫu cho các cách tiếp cận khác nhau, dựa trên bộ sưu tập các 10,000
đối tượng đầu vào , tính trung bình trên 10 lần chạy; các số tuyệt đối không quan trọng và thay đổi dựa trên nhiều yếu tố, nhưng nó sẽ mang lại cho bạn cảm giác về hiệu suất tương đối (thời gian đến từ máy ảo Windows 10 lõi đơn:
Quan trọng
Hiệu suất tương đối thay đổi dựa trên việc các đối tượng đầu vào là các thể hiện của các loại .NET thông thường (ví dụ: như đầu ra theo Get-ChildItem
) hoặc các [pscustomobject]
thể hiện (ví dụ như là đầu ra của Convert-FromCsv
).
Lý do là các [pscustomobject]
thuộc tính được PowerShell quản lý động và nó có thể truy cập chúng nhanh hơn các thuộc tính thông thường của loại .NET thông thường (được xác định tĩnh). Cả hai kịch bản được đề cập dưới đây.
Các thử nghiệm sử dụng các bộ sưu tập đã có trong bộ nhớ đầy đủ làm đầu vào, để tập trung vào hiệu suất trích xuất thuộc tính thuần túy. Với lệnh gọi lệnh cmdlet / hàm làm đầu vào, sự khác biệt về hiệu suất thường sẽ ít rõ rệt hơn, vì thời gian bên trong cuộc gọi đó có thể chiếm phần lớn thời gian sử dụng.
Để đơn giản, bí danh %
được sử dụng cho ForEach-Object
lệnh ghép ngắn.
Kết luận chung , áp dụng cho cả loại .NET thông thường và [pscustomobject]
đầu vào:
Bảng liệt kê thành viên ( $collection.Name
) và foreach ($obj in $collection)
các giải pháp nhanh nhất , nhanh hơn 10 lần so với giải pháp dựa trên đường ống nhanh nhất.
Đáng ngạc nhiên, % Name
thực hiện tồi tệ hơn nhiều % { $_.Name }
- xem vấn đề GitHub này .
PowerShell Core luôn vượt trội so với Windows Powershell tại đây.
Thời gian với các loại .NET thông thường :
- 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
Comparing property-value extraction methods with 10000 input objects, averaged over 10 runs...
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
Kết luận:
- Trong PowerShell Core ,
.ForEach('Name')
rõ ràng vượt trội hơn .ForEach({ $_.Name })
. Trong Windows PowerShell, thật kỳ lạ, cái sau nhanh hơn, mặc dù chỉ là một chút.
Thời gian với các [pscustomobject]
trường hợp :
- 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
Kết luận:
Lưu ý làm thế nào với [pscustomobject]
đầu vào .ForEach('Name')
bằng cách vượt xa biến thể dựa trên khối tập lệnh , .ForEach({ $_.Name })
.
Tương tự, [pscustomobject]
đầu vào làm cho đường ống Select-Object -ExpandProperty Name
nhanh hơn, trong Windows PowerShell gần như ngang bằng .ForEach({ $_.Name })
, nhưng trong PowerShell Core vẫn chậm hơn khoảng 50%.
Tóm lại: Với ngoại lệ kỳ lạ % Name
, với [pscustomobject]
các phương thức tham chiếu dựa trên chuỗi các thuộc tính vượt trội hơn các thuộc tính dựa trên scriptblock.
Mã nguồn cho các bài kiểm tra :
Ghi chú:
Tải xuống chức năng Time-Command
từ Gist này để chạy các thử nghiệm này.
Thay vào đó, thiết lập $useCustomObjectInput
để $true
đo bằng các [pscustomobject]
trường hợp.
$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 home dir. tree
# may be less than $count
$objects = Get-ChildItem -Recurse $HOME | 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*
$results = @($objects | %{ $_.Name })
. Điều này có thể thuận tiện hơn để gõ vào dòng lệnh đôi khi, mặc dù tôi nghĩ rằng câu trả lời của Scott nói chung là tốt hơn.