PowerShell में किसी सरणी के सभी ऑब्जेक्ट पर एक गुण का मान चुनें


134

मान लें कि हमारे पास वस्तुओं की एक सरणी है $ ऑब्जेक्ट। मान लें कि इन ऑब्जेक्ट्स में "नाम" गुण है।

यही है जो मैं करना चाहता हूं

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

यह काम करता है, लेकिन क्या इसे बेहतर तरीके से किया जा सकता है?

अगर मैं ऐसा कुछ करता हूं:

 $results = objects | select Name

$resultsएक नाम संपत्ति रखने वाली वस्तुओं की एक सरणी है। मुझे नाम का एक सरणी समाहित करने के लिए $ परिणाम चाहिए।

क्या कोई बेहतर तरीका है?


4
पूर्णता के लिए, आप अपने मूल कोड से "+ =" भी निकाल सकते हैं, ताकि फ़ॉरचेक केवल नाम का चयन करे $results = @($objects | %{ $_.Name }):। यह कई बार कमांड लाइन पर टाइप करने के लिए सुविधाजनक हो सकता है, हालांकि मुझे लगता है कि स्कॉट का जवाब आम तौर पर बेहतर है।
सम्राट XLII

1
@Em KingXLII: अच्छी बात है, और PSv3 + में आप इसे सरल भी कर सकते हैं:$objects | % Name
mklement0

जवाबों:


212

मुझे लगता है कि आप के ExpandPropertyपैरामीटर का उपयोग करने में सक्षम हो सकते हैं Select-Object

उदाहरण के लिए, वर्तमान निर्देशिका की सूची प्राप्त करने के लिए और नाम की संपत्ति प्रदर्शित करने के लिए, निम्नलिखित में से कोई एक करना होगा:

ls | select -Property Name

यह अभी भी DirectoryInfo या FileInfo ऑब्जेक्ट लौटा रहा है। आप हमेशा पाइप के माध्यम से गेट-मेंबर (उर्फ gm) को पाइप लाइन के माध्यम से आने वाले प्रकार का निरीक्षण कर सकते हैं ।

ls | select -Property Name | gm

इसलिए, जिस वस्तु को आप देख रहे हैं, उस प्रकार की वस्तु का विस्तार करने के लिए , आप निम्नलिखित कार्य कर सकते हैं:

ls | select -ExpandProperty Name

आपके मामले में, आप केवल निम्नलिखित कर सकते हैं कि एक चर स्ट्रिंग की एक सरणी हो, जहां तार नाम संपत्ति हैं:

$objects = ls | select -ExpandProperty Name

73

और भी आसान उपाय के रूप में, आप बस इस्तेमाल कर सकते हैं:

$results = $objects.Name

जो $resultsतत्वों के सभी 'नाम' संपत्ति मूल्यों की एक सरणी के साथ भरना चाहिए $objects


ध्यान दें कि यह काम नहीं करता है Exchange Management Shell। एक्सचेंज का उपयोग जब हम उपयोग करने की आवश्यकता$objects | select -Property Propname, OtherPropname
Bassie

2
@ बासी: अपने सदस्यों के मूल्यों को एक सरणी के रूप में प्राप्त करने के लिए संग्रह स्तर पर एक संपत्ति तक पहुँचना सदस्य गणना कहलाता है और एक PSv3 + सुविधा है ; संभवतः, आपका एक्सचेंज मैनेजमेंट शेल PSv2 है।
mklement0

32

किस दृष्टिकोण और एक प्रदर्शन की तुलना का उपयोग करने के मार्गदर्शन के साथ मदद करने के लिए मदद करने के लिए आकर्षक जवाब ।

  • एक पाइपलाइन के बाहर , उपयोग (PSv3 +):

    $ वस्तुओं नाम
    जैसा कि rageandqq के उत्तर में प्रदर्शित किया गया है , जो कि वाक्य-रचना सरल और बहुत तेज है

    • एक सरणी के रूप में अपने सदस्यों के मूल्यों को प्राप्त करने के लिए संग्रह स्तर पर एक संपत्ति तक पहुंच को सदस्य गणन कहा जाता है और एक PSv3 + सुविधा है।
    • वैकल्पिक रूप से, PSv2 में , उस foreach स्टेटमेंट का उपयोग करें , जिसका आउटपुट आप सीधे एक वेरिएबल को भी असाइन कर सकते हैं:
      $ परिणाम = foreach ($ ऑब्जेक्ट में $ obj) {$ obj.Name}
    • ट्रेडऑफ़ :
      • इनपुट संग्रह और आउटपुट सरणी दोनों को एक पूरे के रूप में मेमोरी में फिट होना चाहिए ।
      • यदि इनपुट संग्रह स्वयं एक कमांड (पाइपलाइन) (जैसे) का परिणाम है (Get-ChildItem).Name, तो उस कमांड को पहले पूरा करना होगा ताकि परिणामी सरणी के तत्वों तक पहुंचा जा सके।
  • एक पाइपलाइन में जहां परिणाम को आगे संसाधित किया जाना चाहिए या परिणाम स्मृति में एक पूरे के रूप में फिट नहीं होते हैं, उपयोग करें:

    $ वस्तुओं | Select-Object -ExpandProperty नाम

    • स्कॉट साद के उत्तर-ExpandProperty में इसकी आवश्यकता बताई गई है ।
    • आपको एक-एक-एक प्रसंस्करण के सामान्य पाइपलाइन लाभ मिलते हैं, जो आम तौर पर तुरंत उत्पादन का उत्पादन करता है और मेमोरी उपयोग को स्थिर रखता है (जब तक कि आप अंततः परिणाम को किसी भी तरह इकट्ठा नहीं करते हैं)।
    • ट्रेडऑफ़ :
      • पाइपलाइन का उपयोग तुलनात्मक रूप से धीमा है

के लिए छोटे इनपुट संग्रह (सरणियों), तो आप शायद अंतर नोटिस नहीं , है, और, विशेष रूप से कमांड लाइन पर कभी-कभी आदेश टाइप करने के लिए सक्षम किया जा रहा आसानी से ज्यादा महत्वपूर्ण है।


यहां एक आसान-प्रकार का विकल्प है , जो हालांकि सबसे धीमा दृष्टिकोण है ; यह सरलीकृत ForEach-Objectवाक्यविन्यास का उपयोग करता है जिसे ऑपरेशन स्टेटमेंट (फिर, PSv3 +) कहा जाता है :; उदाहरण के लिए, निम्नलिखित PSv3 + समाधान मौजूदा कमांड में जोड़ना आसान है:

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

पूर्णता के लिए के लिए: अल्पज्ञात PSv4 + .ForEach() सरणी विधि , अधिक comprehensivel में चर्चा इस लेख , है अभी तक एक और विकल्प :

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

# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
  • यह दृष्टिकोण एक ही ट्रेडऑफ़ के साथ सदस्य गणन के समान है, सिवाय इसके कि पाइपलाइन तर्क लागू नहीं होता है; यह थोड़ा धीमा है , हालांकि अभी भी पाइप लाइन की तुलना में अधिक तेज है।

  • नाम ( स्ट्रिंग तर्क) द्वारा एकल गुण मान निकालने के लिए , यह समाधान सदस्य गणना के साथ सममूल्य पर है (हालांकि बाद का वाक्य सरल है)।

  • स्क्रिप्ट ब्लॉक संस्करण , मनमाने ढंग से की अनुमति देता है परिवर्तनों ; यह एक तेज - ऑल-इन-मेमोरी-एट-एक बार - पाइपलाइन-आधारित ForEach-Object cmdlet ( %) का विकल्प है


विभिन्न दृष्टिकोणों के प्रदर्शन की तुलना करना

यहां विभिन्न दृष्टिकोणों के लिए नमूना समय दिया गया है, 10,000वस्तुओं के एक इनपुट संग्रह के आधार पर , औसतन 10 रन; निरपेक्ष संख्याएँ महत्वपूर्ण नहीं हैं और कई कारकों के आधार पर अलग-अलग हैं, लेकिन यह आपको सापेक्ष प्रदर्शन का एहसास दिलाता है (एकल-कोर विंडोज़ 10 वीएम से आने वाला समय:

जरूरी

  • सापेक्ष प्रदर्शन इस बात के आधार पर भिन्न होता है कि क्या इनपुट ऑब्जेक्ट नियमित .NET प्रकार के उदाहरण हैं (उदाहरण के लिए, आउटपुट द्वारा Get-ChildItem) या [pscustomobject]उदाहरण (उदाहरण के लिए, आउटपुट द्वारा Convert-FromCsv)।
    इसका कारण यह है कि [pscustomobject]गुणों को गतिशील रूप से PowerShell द्वारा प्रबंधित किया जाता है, और यह उन्हें (सांख्यिकीय रूप से परिभाषित) नियमित .NET प्रकार के नियमित गुणों की तुलना में अधिक तेज़ी से एक्सेस कर सकता है। दोनों परिदृश्य नीचे कवर किए गए हैं।

  • परीक्षण इनपुट के रूप में पहले से ही इन-मेमोरी-इन-फुल संग्रह का उपयोग करते हैं, ताकि शुद्ध संपत्ति निष्कर्षण प्रदर्शन पर ध्यान केंद्रित किया जा सके। इनपुट के रूप में एक स्ट्रीमिंग cmdlet / फ़ंक्शन कॉल के साथ, प्रदर्शन अंतर आम तौर पर बहुत कम स्पष्ट होंगे, क्योंकि उस कॉल के अंदर बिताया गया समय खर्च किए गए समय के बहुमत के लिए हो सकता है।

  • संक्षिप्तता के लिए, उपनाम %का उपयोग ForEach-Objectcmdlet के लिए किया जाता है ।

सामान्य निष्कर्ष , दोनों नियमित .NET प्रकार और [pscustomobject]इनपुट पर लागू होते हैं :

  • सदस्य-गणना ( $collection.Name) और foreach ($obj in $collection)समाधान सबसे तेज पाइपलाइन-आधारित समाधान की तुलना में 10 या उससे अधिक तेजी से सबसे तेज हैं।

  • हैरानी की बात है, की % Nameतुलना में बहुत खराब प्रदर्शन करता है % { $_.Name }- इस GitHub मुद्दे को देखें ।

  • PowerShell Core यहां विंडोज पॉवर्सशेल को लगातार बेहतर बनाता है।

नियमित .NET प्रकार के साथ समय :

  • PowerShell Core v7.0.0-पूर्वावलोकन .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
  • विंडोज पॉवरशेल 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

निष्कर्ष:

  • पावरशेल कोर में , .ForEach('Name')स्पष्ट रूप से बेहतर प्रदर्शन .ForEach({ $_.Name })। विंडोज पॉवरशेल में, उत्सुकता से, बाद में तेजी से होता है, यद्यपि केवल इतना ही।

[pscustomobject]उदाहरणों के साथ समय :

  • PowerShell Core v7.0.0-पूर्वावलोकन .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
  • विंडोज पॉवरशेल 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')दूर Outperforms द्वारा आधारित स्क्रिप्ट ब्लॉक संस्करण .ForEach({ $_.Name })

  • इसी तरह, [pscustomobject]इनपुट Select-Object -ExpandProperty Nameविंडोज आधारित पावरशेल में लगभग पाईप पर आधारित पाइप लाइन को तेज बनाता है .ForEach({ $_.Name }), लेकिन पावरशेल कोर में अभी भी लगभग 50% धीमा है।

  • संक्षेप में: के अजीब अपवाद के % Nameसाथ, [pscustomobject]गुणों को संदर्भित करने के स्ट्रिंग-आधारित तरीकों के साथ स्क्रिप्टब्लॉक-आधारित वाले को बेहतर बनाते हैं।


परीक्षणों के लिए स्रोत कोड :

ध्यान दें:

  • इन परीक्षणों को चलाने के लिए इस GistTime-Command से फ़ंक्शन डाउनलोड करें ।

  • सेट $useCustomObjectInputकरने के लिए $trueके साथ मापने के लिए [pscustomobject]बजाय उदाहरणों।

$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*

1

सावधानी, सदस्य गणन केवल तभी काम करता है जब संग्रह में स्वयं समान नाम का कोई सदस्य न हो। इसलिए यदि आपके पास FileInfo ऑब्जेक्ट्स की एक सरणी है, तो आप उपयोग करके फ़ाइल की लंबाई का एक सरणी प्राप्त नहीं कर सकते

 $files.length # evaluates to array length

और इससे पहले कि आप "अच्छी तरह से" कहें, इस पर विचार करें। यदि आपके पास एक क्षमता संपत्ति के साथ वस्तुओं की एक सरणी थी तो

 $objarr.capacity

चाहेंगे काम ठीक जब तक $ objarr वास्तव में थे, न कि किसी [सरणी] लेकिन, उदाहरण के लिए, एक [ArrayList]। इसलिए सदस्य गणना का उपयोग करने से पहले आपको अपने संग्रह वाले ब्लैक बॉक्स के अंदर देखना पड़ सकता है।

(मध्यस्थों पर ध्यान दें: यह rageandqq के उत्तर पर एक टिप्पणी होनी चाहिए, लेकिन मेरे पास अभी तक पर्याप्त प्रतिष्ठा नहीं है।)


यह एक अच्छी बात है; यह GitHub सुविधा अनुरोध सदस्य गणन के लिए एक अलग वाक्यविन्यास के लिए कहता है। नाम टकराव के लिए समाधान के .ForEach()रूप में सरणी विधि का उपयोग करना है:$files.ForEach('Length')
mklement0
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.