كيف يمكنني تمرير المعلمات المسمى مع الاستدعاء؟

StackOverflow https://stackoverflow.com/questions/4225748

  •  26-09-2019
  •  | 
  •  

سؤال

لديّ برنامج نصي يمكنني تشغيله عن بُعد عن طريق الاستدعاء

Invoke-Command -ComputerName (Get-Content C:\Scripts\Servers.txt) `
               -FilePath C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1

طالما أستخدم المعلمات الافتراضية ، فإنه يعمل بشكل جيد. ومع ذلك ، يحتوي البرنامج النصي على 2 معلمات [Switch] (-Debug و -clear)

كيف يمكنني تمرير المعلمات المحولة عبر الرائد الاستدعاء؟ لقد جربت -ArgumentList ولكني أتلقى أخطاء ، لذا يجب أن يكون لدي بناء جملة خطأ أو شيء من هذا القبيل. أي مساعدة يحظى بتقدير كبير.

هل كانت مفيدة؟

المحلول

-ArgumentList يعتمد على الاستخدام مع ScriptBlock الأوامر ، مثل:

Invoke-Command -Cn (gc Servers.txt) {param($Debug=$False, $Clear=$False) C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 } -ArgumentList $False,$True

عندما تسميها مع أ -File لا يزال يمرر المعلمات مثل صفيف غبي. لقد قدمت أ طلب المواصفات لإضافة ذلك إلى الأمر (يرجى التصويت على ذلك).

بحيث يكون لديك خيارين:

إذا كان لديك برنامج نصي يبدو هكذا ، في موقع الشبكة يمكن الوصول إليه من الجهاز البعيد (لاحظ ذلك -Debug ضمني لأنه عندما أستخدم Parameter السمة ، يحصل البرنامج النصي على cmdletbinding ضمنيًا ، وبالتالي ، كل المعلمات الشائعة):

param(
   [Parameter(Position=0)]
   $one
,
   [Parameter(Position=1)]
   $two
,
   [Parameter()]
   [Switch]$Clear
)

"The test is for '$one' and '$two' ... and we $(if($DebugPreference -ne 'SilentlyContinue'){"will"}else{"won't"}) run in debug mode, and we $(if($Clear){"will"}else{"won't"}) clear the logs after."

دون أن تعلق على معنى $Clear ... إذا أردت التذرع أنه يمكنك استخدام أي مما يلي Invoke-Command بناء الجملة:

icm -cn (gc Servers.txt) { 
    param($one,$two,$Debug=$False,$Clear=$False)
    C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 @PSBoundParameters
} -ArgumentList "uno", "dos", $false, $true

في هذا واحد ، أقوم بتكرار جميع المعلمات التي أهتم بها في ScriptBlock حتى أتمكن من تمرير القيم. إذا تمكنت PSBoundParameters, ، يمكنني فقط تمرير تلك التي أحتاج إليها. في المثال الثاني أدناه ، سأقوم بتمرير $ clear ، فقط لإظهار كيفية تمرير معلمات التبديل:

icm -cn $Env:ComputerName { 
    param([bool]$Clear)
    C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $(Test-Path $Profile)

الخيار الآخر

إذا كان البرنامج النصي على جهازك المحلي ، ولم ترغب في تغيير المعلمات لتكون موضعية ، أو تريد تحديد المعلمات التي هي معلمات شائعة (لذلك لا يمكنك التحكم فيها) ستحتاج إلى الحصول على محتوى هذا البرنامج النصي وتضمينه في الخاص بك ScriptBlock:

$script = [scriptblock]::create( @"
param(`$one,`$two,`$Debug=`$False,`$Clear=`$False)
&{ $(Get-Content C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -delimiter ([char]0)) } @PSBoundParameters
"@ )

Invoke-Command -Script $script -Args "uno", "dos", $false, $true

بوستسكريبت:

إذا كنت بحاجة حقًا إلى المرور في متغير لاسم البرنامج النصي ، فسوف يعتمد ما تفعله على ما إذا كان المتغير محدد محليًا أو عن بُعد. بشكل عام ، إذا كان لديك متغير $Script أو متغير البيئة $Env:Script مع اسم البرنامج النصي ، يمكنك تنفيذه باستخدام مشغل المكالمات (&): &$Script أو &$Env:Script

إذا كان متغير البيئة محدد بالفعل على الكمبيوتر البعيد ، فهذا كل ما هو موجود عليه. إذا كان محلي متغير ، ثم يتعين عليك تمريره إلى كتلة البرنامج النصي عن بُعد:

Invoke-Command -cn $Env:ComputerName { 
    param([String]$Script, [bool]$Clear)
    &$Script "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $ScriptPath, $(Test-Path $Profile)

نصائح أخرى

كان حلي لهذا هو كتابة كتلة البرنامج النصي ديناميكيًا مع [scriptblock]:Create:

# Or build a complex local script with MARKERS here, and do substitutions
# I was sending install scripts to the remote along with MSI packages
# ...for things like Backup and AV protection etc.

$p1 = "good stuff"; $p2 = "better stuff"; $p3 = "best stuff"; $etc = "!"
$script = [scriptblock]::Create("MyScriptOnRemoteServer.ps1 $p1 $p2 $etc")
#strings get interpolated/expanded while a direct scriptblock does not

# the $parms are now expanded in the script block itself
# ...so just call it:
$result = invoke-command $computer -script $script

كان تمرير الحجج محبطًا للغاية ، حيث كان يحاول العديد من الأساليب ، على سبيل المثال ،
-arguments, $using:p1, ، وما إلى ذلك وهذا عمل فقط كما هو مطلوب دون أي مشاكل.

بما أنني أتحكم في المحتويات والتوسع المتغير للسلسلة التي تنشئ [scriptblock] (أو ملف البرنامج النصي) بهذه الطريقة ، لا توجد مشكلة حقيقية مع تعويذة "الاحتجاج".

(لا ينبغي أن يكون بهذه الصعوبة. :))

أظن أنها ميزة جديدة منذ أن تم إنشاء هذا المنشور - تمرير معلمات إلى كتلة البرنامج النصي باستخدام $ باستخدام: var. بعد ذلك ، فإن الأم البسيطة لتمرير معلمات شريطة أن يكون البرنامج النصي بالفعل على الجهاز أو في موقع شبكة معروف بالنسبة للجهاز

أخذ المثال الرئيسي سيكون:

icm -cn $Env:ComputerName { 
    C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -one "uno" -two "dos" -Debug -Clear $Using:Clear
}

كنت بحاجة إلى شيء لاتصال البرامج النصية باستخدام المعلمات المسماة. لدينا سياسة عدم استخدام المواقع الترتيبية للمعلمات وتتطلب اسم المعلمة.

يشبه مقاربي تلك المذكورة أعلاه ولكنه يحصل على محتوى ملف البرنامج النصي الذي تريد الاتصال به ويرسل كتلة معلمة تحتوي على المعلمات والقيم.

واحدة من مزايا ذلك هي أنه يمكنك اختياريا اختيار المعلمات التي يجب إرسالها إلى ملف البرنامج النصي الذي يسمح للمعلمات غير الإلزامية مع الافتراضات.

على افتراض أن هناك برنامج نصي يسمى "myscript.ps1" في المسار المؤقت الذي يحتوي على كتلة المعلمة التالية:

[CmdletBinding(PositionalBinding = $False)]
param
(
    [Parameter(Mandatory = $True)] [String] $MyNamedParameter1,
    [Parameter(Mandatory = $True)] [String] $MyNamedParameter2,
    [Parameter(Mandatory = $False)] [String] $MyNamedParameter3 = "some default value"
)

هذه هي الطريقة التي أسميها هذا البرنامج النصي من نص آخر:

$params = @{
    MyNamedParameter1 = $SomeValue
    MyNamedParameter2 = $SomeOtherValue
}

If ($SomeCondition)
{
    $params['MyNamedParameter3'] = $YetAnotherValue
}

$pathToScript = Join-Path -Path $env:Temp -ChildPath MyScript.ps1

$sb = [scriptblock]::create(".{$(Get-Content -Path $pathToScript -Raw)} $(&{
        $args
} @params)")
Invoke-Command -ScriptBlock $sb

لقد استخدمت هذا في الكثير من السيناريوهات وهو يعمل بشكل جيد. شيء واحد تحتاج إلى القيام به أحيانًا هو وضع اقتباسات حول كتلة تعيين قيمة المعلمة. هذا هو الحال دائمًا عندما تكون هناك مسافات في القيمة.

على سبيل المثال ، يتم استخدام كتلة param هذه لاستدعاء البرنامج النصي الذي ينسخ الوحدات المختلفة في الموقع القياسي الذي يستخدمه PowerShell C:\Program Files\WindowsPowerShell\Modules الذي يحتوي على حرف الفضاء.

$params = @{
        SourcePath      = "$WorkingDirectory\Modules"
        DestinationPath = "'$(Join-Path -Path $([System.Environment]::GetFolderPath('ProgramFiles')) -ChildPath 'WindowsPowershell\Modules')'"
    }

أتمنى أن يساعدك هذا!

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top