क्या पॉवर्सशेल समानांतर में कमांड चला सकता है?


125

मेरे पास छवियों की एक गुच्छा पर कुछ बैच प्रोसेसिंग करने के लिए एक पॉवरशेल स्क्रिप्ट है और मैं कुछ समानांतर प्रसंस्करण करना चाहूंगा। पॉवर्सशेल में कुछ बैकग्राउंड प्रोसेसिंग ऑप्शन जैसे स्टार्ट-जॉब, वेट-जॉब आदि देखने को मिलते हैं, लेकिन समानांतर काम करने के लिए मुझे जो एकमात्र अच्छा संसाधन मिला, वह एक स्क्रिप्ट का टेक्स्ट लिखकर उन्हें चलाने का था ( PowerShell Multithreading )

आदर्श रूप में, मैं कुछ उदाहरण के लिए .net 4 में समान रूप से foreach करना चाहूंगा।

कुछ बहुत अच्छा लगता है:

foreach-parallel -threads 4 ($file in (Get-ChildItem $dir))
{
   .. Do Work
}

शायद मैं सिर्फ # सी करने के लिए नीचे छोड़ने से बेहतर होगा ...


tl; dr; receive-job (wait-job ($a = start-job { "heyo!" })); remove-job $a या $a = start-job { "heyo!" }; wait-job $a; receive-job $a; remove-job $aयह भी ध्यान दें कि यदि आप receive-jobनौकरी समाप्त होने से पहले फोन करते हैं, तो आपको कुछ भी नहीं मिल सकता है।
एंड्रयू

इसके अलावा(get-job $a).jobstateinfo.state;
एंड्रयू

जवाबों:


99

आप बैकग्राउंड जॉब्स का उपयोग करके Powershell 2 में समानांतर नौकरियों को निष्पादित कर सकते हैं । की जाँच करें प्रारंभ-नौकरी और अन्य नौकरी cmdlets।

# Loop through the server list
Get-Content "ServerList.txt" | %{

  # Define what each job does
  $ScriptBlock = {
    param($pipelinePassIn) 
    Test-Path "\\$pipelinePassIn\c`$\Something"
    Start-Sleep 60
  }

  # Execute the jobs in parallel
  Start-Job $ScriptBlock -ArgumentList $_
}

Get-Job

# Wait for it all to complete
While (Get-Job -State "Running")
{
  Start-Sleep 10
}

# Getting the information back from the jobs
Get-Job | Receive-Job

3
इसलिए मैंने कई बार इस सुझाव की कोशिश की, लेकिन ऐसा लगता है कि मेरे चर का सही ढंग से विस्तार नहीं हो रहा है। उसी उदाहरण का उपयोग करने के लिए, जब यह रेखा निष्पादित होती है: Test-Path "\\$_\c$\Something"मैं इसे $_वर्तमान आइटम में विस्तार करने की उम्मीद करूंगा । हालाँकि, यह नहीं है। इसके बजाय यह एक खाली मान देता है। यह केवल स्क्रिप्ट ब्लॉक के भीतर से होता है। अगर मैं पहली टिप्पणी के तुरंत बाद उस मूल्य को लिखता हूं, तो यह सही ढंग से काम करता है।
rjg

1
@likwid - साइट के लिए एक अलग प्रश्न की तरह लगता है
स्टीव टाउनसेंड

मैं उस नौकरी के आउटपुट को कैसे देख सकता हूं जो पृष्ठभूमि में चल रहा है?
सिंपल जीयू

@SimpleGuy - आउटपुट कैप्चर के बारे में जानकारी के लिए यहाँ देखें - stackoverflow.com/questions/15605095/… - ऐसा नहीं लगता कि आप इस मज़बूती को तब तक देख सकते हैं जब तक कि बैकग्राउंड जॉब पूरी नहीं हो जाती।
स्टीव टाउनसेंड

@SteveTownsend धन्यवाद! वास्तव में आउटपुट देखना स्क्रीन पर इतना अच्छा नहीं है। देरी से आता है, इसलिए मेरे लिए उपयोगी नहीं है। इसके बजाय मैंने नए टर्मिनल (शेल) पर एक प्रक्रिया शुरू की, इसलिए अब प्रत्येक प्रक्रिया अलग-अलग टर्मिनल पर चल रही है, जो प्रगति के दृष्टिकोण को बहुत बेहतर और अधिक स्वच्छ बनाती है।
सिंपल जीयू

98

स्टीव टाउनसेंड का उत्तर सिद्धांत में सही है, लेकिन व्यवहार में नहीं जैसा कि @likwid ने बताया। मेरा संशोधित कोड जॉब- कॉन्ट्रैक्ट बैरियर को ध्यान में रखता है - उस क्रॉस को डिफॉल्ट रूप से क्रॉस करना! इस $_प्रकार स्वचालित चर का उपयोग लूप में किया जा सकता है, लेकिन इसे स्क्रिप्ट ब्लॉक के भीतर सीधे उपयोग नहीं किया जा सकता है क्योंकि यह कार्य द्वारा निर्मित एक अलग संदर्भ के अंदर है।

माता-पिता के संदर्भ से बच्चे के संदर्भ में चर पास करने के लिए, इसे भेजने के लिए -ArgumentListपैरामीटर का उपयोग करें Start-Jobऔर paramइसे प्राप्त करने के लिए स्क्रिप्ट ब्लॉक के अंदर का उपयोग करें ।

cls
# Send in two root directory names, one that exists and one that does not.
# Should then get a "True" and a "False" result out the end.
"temp", "foo" | %{

  $ScriptBlock = {
    # accept the loop variable across the job-context barrier
    param($name) 
    # Show the loop variable has made it through!
    Write-Host "[processing '$name' inside the job]"
    # Execute a command
    Test-Path "\$name"
    # Just wait for a bit...
    Start-Sleep 5
  }

  # Show the loop variable here is correct
  Write-Host "processing $_..."

  # pass the loop variable across the job-context barrier
  Start-Job $ScriptBlock -ArgumentList $_
}

# Wait for all to complete
While (Get-Job -State "Running") { Start-Sleep 2 }

# Display output from all jobs
Get-Job | Receive-Job

# Cleanup
Remove-Job *

(मैं आमतौर पर समर्थन साक्ष्य के रूप में पॉवरशेल डॉक्यूमेंट का संदर्भ प्रदान करना पसंद करता हूं लेकिन, अफसोस, मेरी खोज निरर्थक रही है। यदि आपको पता है कि संदर्भ पृथक्करण कहां प्रलेखित है, तो मुझे बताने के लिए यहां एक टिप्पणी पोस्ट करें!)


इस उत्तर के लिए धन्यवाद। मैंने आपके समाधान का उपयोग करने की कोशिश की, लेकिन मैं इसे पूरी तरह से काम करने में असमर्थ था। क्या आप यहां मेरे प्रश्न पर एक नज़र डाल सकते हैं: stackoverflow.com/questions/28509659/…
डेविड का कहना है मोनिका

वैकल्पिक रूप से, एक अलग स्क्रिप्ट फ़ाइल को लागू करना बहुत आसान है। बस का उपयोग करेंStart-Job -FilePath script.ps1 -ArgumentList $_
चाड Zawistowski

एक वैकल्पिक दृष्टिकोण स्क्रिप्ट पीढ़ी का प्रारंभिक पास करना है, जहां चर विस्तार के अलावा कुछ भी नहीं किया जा रहा है, और फिर उत्पन्न स्क्रिप्ट को समानांतर में लागू किया जाता है। मेरे पास एक छोटा सा उपकरण है जिसे स्क्रिप्ट पीढ़ी के लिए अनुकूलित किया जा सकता है, हालांकि यह स्क्रिप्ट पीढ़ी का समर्थन करने के लिए कभी नहीं था। आप इसे यहाँ देख सकते हैं ।
वाल्टर मिती

यह काम। लेकिन मैं ScriptBlock से लाइव फीड आउटपुट स्ट्रीम प्राप्त नहीं कर सकता। ScriptBlock के वापस आने पर आउटपुट केवल प्रिंट हो जाता है।
वोटशेन

8

http://gallery.technet.microsoft.com/scriptcenter/Invoke-Async-Allows-you-to-83b0c9f0

मैंने एक इनवॉइस-एसिंक्स बनाया जो आपको एक ही समय में कई स्क्रिप्ट ब्लॉक / cmdlets / फ़ंक्शन चलाने की अनुमति देता है। यह छोटी नौकरियों (100 की मशीनों के खिलाफ सबनेट स्कैन या wmi क्वेरी) के लिए बहुत अच्छा है क्योंकि स्टार्ट-जॉब के स्टार्टअप समय बनाम रनस्पेस बनाने के लिए ओवरहेड बहुत कठोर है। इसका उपयोग ऐसे किया जा सकता है।

स्क्रिप्टब्लॉक के साथ,

$sb = [scriptblock] {param($system) gwmi win32_operatingsystem -ComputerName $system | select csname,caption} 

$servers = Get-Content servers.txt 

$rtn = Invoke-Async -Set $server -SetParam system  -ScriptBlock $sb

बस cmdlet / फ़ंक्शन

$servers = Get-Content servers.txt 

$rtn = Invoke-Async -Set $servers -SetParam computername -Params @{count=1} -Cmdlet Test-Connection -ThreadCount 50

8

इन दिनों इसके कई उत्तर हैं:

  1. नौकरियां (या पीएस 6/7 या मॉड्यूल में थ्रेड्सर)
  2. स्टार्ट-प्रक्रिया
  3. workflows
  4. एक अन्य रनस्पेस के साथ शक्तियां एपीआई
  5. कई कंप्यूटरों के साथ इनवोक-कमांड, जो सभी लोकलहोस्ट हो सकते हैं (एडमिन होना है)
  6. ISE, या दूरस्थ पॉवर्सशेल ISE ​​टैब में कई सत्र (रनस्पेस) टैब
  7. Powershell 7 में foreach-object -parallel# 4 के विकल्प के रूप में है

यहाँ सचमुच के साथ वर्कफ़्लोज़ है, एक अपरिपक्व-समानांतर:

workflow work {
  foreach -parallel ($i in 1..3) { 
    sleep 5 
    "$i done" 
  }
}

work

3 done
1 done
2 done

या एक समानांतर ब्लॉक के साथ एक वर्कफ़्लो:

function sleepfor($time) { sleep $time; "sleepfor $time done"}

workflow work {
  parallel {
    sleepfor 3
    sleepfor 2
    sleepfor 1
  }
  'hi'
}

work 

sleepfor 1 done
sleepfor 2 done
sleepfor 3 done
hi

यहाँ अपस्थानिक उदाहरण के साथ एक एपीआई है:

$a =  [PowerShell]::Create().AddScript{sleep 5;'a done'}
$b =  [PowerShell]::Create().AddScript{sleep 5;'b done'}
$c =  [PowerShell]::Create().AddScript{sleep 5;'c done'}
$r1,$r2,$r3 = ($a,$b,$c).begininvoke() # run in background
$a.EndInvoke($r1); $b.EndInvoke($r2); $c.EndInvoke($r3) # wait
($a,$b,$c).streams.error # check for errors
($a,$b,$c).dispose() # clean

a done
b done
c done

7

पृष्ठभूमि की नौकरियां सेटअप के लिए महंगी हैं और पुन: प्रयोज्य नहीं हैं। PowerShell MVP Oisin Grehan में PowerShell मल्टी-थ्रेडिंग का एक अच्छा उदाहरण है।

(10/25/2010 साइट नीचे है, लेकिन वेब आर्काइव के माध्यम से सुलभ है)।

मैं यहाँ डेटा लोडिंग रूटीन में उपयोग के लिए अनुकूलित Oisin स्क्रिप्ट का उपयोग कर रहा हूँ:

http://rsdd.codeplex.com/SourceControl/changeset/view/a6cd657ea2be#Invoke-RSDDThreaded.ps1


लिंक रोट ने इस उत्तर के लिए निर्धारित किया है
ल्यूक

4

पिछले उत्तरों को पूरा करने के लिए, आप Wait-Jobसभी नौकरियों के पूरा होने की प्रतीक्षा करने के लिए भी उपयोग कर सकते हैं :

For ($i=1; $i -le 3; $i++) {
    $ScriptBlock = {
        Param (
            [string] [Parameter(Mandatory=$true)] $increment
        )

        Write-Host $increment
    }

    Start-Job $ScriptBlock -ArgumentList $i
}

Get-Job | Wait-Job | Receive-Job

0

Powershell 7 में आप उपयोग कर सकते हैं foreach-वस्तु -Parallel

$Message = "Output:"
Get-ChildItem $dir | ForEach-Object -Parallel {
    "$using:Message $_"
} -ThrottleLimit 4

0

यदि आप नवीनतम क्रॉस प्लेटफ़ॉर्म पॉवरशेल (जो आपको btw चाहिए) https://github.com/powershell/powershell#get-powershell का उपयोग कर रहे हैं , तो आप &समानांतर स्क्रिप्ट चलाने के लिए एकल जोड़ सकते हैं । ( ;क्रमिक रूप से चलाने के लिए उपयोग करें )

मेरे मामले में मुझे समानांतर में 2 एनपीएम स्क्रिप्ट चलाने की आवश्यकता थी: npm run hotReload & npm run dev


आप npm को powershellइसकी स्क्रिप्ट के लिए उपयोग करने के लिए सेटअप भी कर सकते हैं (डिफ़ॉल्ट रूप से यह cmdविंडोज़ पर उपयोग करता है)।

प्रोजेक्ट रूट फ़ोल्डर से चलाएँ: npm config set script-shell pwsh --userconfig ./.npmrc और उसके बाद एकल npm स्क्रिप्ट कमांड का उपयोग करें:npm run start

"start":"npm run hotReload & npm run dev"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.