Soru Bir komut dosyasından argümanlar içeren ikinci bir betik çağırın


Ben PowerShell için nispeten yeniyim ve bir ikinci PowerShell betiğinde bir işleve argüman olarak geçmek istediğim bir dizi isim değeri çiftiyle sonuçlanan bir yapılandırma dosyasını okuyan bir betik var.

Bu yapılandırma dosyasında hangi parametrelerin tasarım zamanında yerleştirileceğini bilmiyorum, bu yüzden bu ikinci PowerShell betiğini çağırmam gereken noktada, sadece bu ikinci komut dosyasının yolunu gösteren bir değişkenim var. yol değişkeni içinde tanımlanan komut dosyasına iletilecek argüman dizisi olan değişken.

Yani, ikinci betiğin yolunu içeren değişken ($ scriptPath), aşağıdaki gibi bir değere sahip olabilir:

"c:\the\path\to\the\second\script.ps1"

Argümanlar ($ argumentList) içeren değişken şöyle görünebilir:

-ConfigFilename "doohickey.txt" -RootDirectory "c:\some\kind\of\path" -Max 11

Bu durumdan, script.ps1'in yürütülmesine tüm argümanların $ argumentList uygulamasından nasıl gelebilirim?

Bu ikinci komut dosyasından herhangi bir yazma-host komutunun, bu ilk komut dosyasının çalıştırıldığı konsola görünmesini istiyorum.

Nokta kaynağı, Invoke-Command, Invoke-İfade ve Start-Job denedim, ancak hata üretmeyen bir yaklaşım bulamadım.

Örneğin, en kolay ilk rotanın aşağıdaki gibi denilen Start-Job'ı denemek olduğunu düşündüm:

Start-Job -FilePath $scriptPath -ArgumentList $argumentList

... ama bu hatayla başarısız oluyor:

System.Management.Automation.ValidationMetadataException:
Attribute cannot be added because it would cause the variable
ConfigFilename with value -ConfigFilename to become invalid.

... bu durumda, "ConfigFilename", ikinci komut dosyası tarafından tanımlanan param listesindeki ilk parametredir ve benim çağrımın, değeri "-ConfigFilename" olarak ayarlamaya çalıştığı açıktır. , değerini ayarlamayın.

Neyi kaçırıyorum?

DÜZENLE:

Tamam, burada sözde betik, invokee.ps1 adlı bir dosyada bir mock-up

Param(
[parameter(Mandatory=$true)]
[alias("rc")]
[string]
[ValidateScript( {Test-Path $_ -PathType Leaf} )]
$ConfigurationFilename,
[alias("e")]
[switch]
$Evaluate,
[array]
[Parameter(ValueFromRemainingArguments=$true)]
$remaining)

function sayHelloWorld()
{
    Write-Host "Hello, everybody, the config file is <$ConfigurationFilename>."
    if ($ExitOnErrors)
    {
        Write-Host "I should mention that I was told to evaluate things."
    }
    Write-Host "I currently live here: $gScriptDirectory"
    Write-Host "My remaining arguments are: $remaining"
    Set-Content .\hello.world.txt "It worked"
}

$gScriptPath = $MyInvocation.MyCommand.Path
$gScriptDirectory = (Split-Path $gScriptPath -Parent)
sayHelloWorld

... ve burada, invokee.ps1 adlı bir dosyada çağıran betiğin bir maketidir:

function pokeTheInvokee()
{
    $scriptPath = (Join-Path -Path "." -ChildPath "invokee.ps1")
    $scriptPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($scriptPath)

    $configPath = (Join-Path -Path "." -ChildPath "invoker.ps1")
    $configPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($configPath)

    $argumentList = @()
    $argumentList += ("-ConfigurationFilename", "`"$configPath`"")
    $argumentList += , "-Evaluate"

    Write-Host "Attempting to invoke-expression with: `"$scriptPath`" $argumentList"
    Invoke-Expression "`"$scriptPath`" $argumentList"
    Invoke-Expression ".\invokee.ps1 -ConfigurationFilename `".\invoker.ps1`" -Evaluate
    Write-Host "Invokee invoked."
}

pokeTheInvokee

İnvoker.ps1'i çalıştırdığımda, şu anda Invoke-Expression'a ilk çağrıyı gerçekleştirdiğim hata budur:

Invoke-Expression : You must provide a value expression on
the right-hand side of the '-' operator.

İkinci çağrı gayet iyi çalışıyor, ancak önemli bir fark, ilk versiyonun yolları boşlukları olan argümanlar kullanıyor olması ve ikincisinin olmaması. Bu yollarda boşlukların varlığını yanlış anladım mı?


44
2017-10-12 00:12


Menşei


Ayrıca, komut dosyalarınızın her birine kaynak sağladıysanız da yardımcı olacaktır. Örneğin, sorununuzu gösteren POC (kavram kanıtı) projesi oluşturun. Daha sonra test edebilir ve onunla oynayabiliriz, belki birileri bir çözüm veya bir çözüm bulabilir. - Neolisk
Evet - Göstermek için mini bir versiyona girmeye çalışıyorum. Yakında yayınlanacak ... - Hoobajoob


Cevaplar:


Aha. Bu, betiğin yolunda boşluklar olmanın basit bir sorunu olduğu ortaya çıktı.

Invoke-İfade satırını değiştirmek için:

Invoke-Expression "& `"$scriptPath`" $argumentList"

... başlaması için yeterliydi. Yardım ve geri bildiriminiz için Neolisk'e teşekkürler!


41
2017-10-12 03:25



Çözdüğüne sevindim. Cevabım yardımcı olsaydı, bunu yapabilirsin. - Neolisk
Cevabınıza eklemek için. Yolda boşluk olması, teklifleri kullanmanız gerektiği anlamına gelir. Tırnaklar, bir komutun tersine, Invoke-Expression ile bir dize olarak ele alınır. Bu nedenle, dizeyi yürütmek istiyorsanız, örneğinizin başlangıcında ve işareti olan "çağrı operatörü" ni kullanmanız gerekir. İşte arama operatörü için bir tanımdır. - Aeropher


Invoke-Expression Mükemmel çalışmalı, sadece doğru şekilde kullandığınızdan emin olun. Davanız için şöyle görünmelidir:

Invoke-Expression "$scriptPath $argumentList"

Bu yaklaşımı Get-Service ile test ettim ve beklendiği gibi çalışıyor gibi görünüyor.


38
2017-10-12 00:52



Invoke-Expression "$ scriptPath $ argumentList" hemen döner ve betiği çalıştırmak için görünmüyor. Eğer Invoke-Expression "powershell $ scriptPath $ argumentList" olarak değiştirirsem, PowerGui sadece asılı görünür. - Hoobajoob
@Hoobajoob: Invoke-Expression hemen geri dönmez, çağrılan komut dosyası sona erinceye kadar sabırla bekler - Sadece bir POC projesinde test ettim. Hedeflediğiniz komut dosyasında bir sorun olmalı. - Neolisk
Hm - Bu ikinci komut dosyasını toplu iş dosyaları, görsel stüdyosu IDE, DOS komut satırları ve powershell konsol oturumlarından birçok farklı şekilde çalıştırdım, ancak bu, başka bir powershell komut dosyasından çalıştırmayı ilk denedim. Komut dosyasında, Invoke-Expression aracılığıyla başarılı bir şekilde başlatılmasını engelleyecek ne tür şeyler olabilirdi? - Hoobajoob
Bu yalnızca argüman listesindeki nesnelerin hepsi doğru olsa tipte bir dizge ise işe yarayacaktır? - Backwards_Dave
@Backwards_Dave: $scriptPath ve $argumentList burada dizeler. Dize, dize olmayan bir argüman içerebilir (bir dize olarak yazılır). Umarım sorunuza cevap verir. - Neolisk


Çok daha basit aslında:

Yöntem 1:

Invoke-Expression $scriptPath $argumentList

Yöntem 2: 

& $scriptPath $argumentList

Yöntem 3:

$scriptPath $argumentList

Senin içinde boşluk varsa scriptPathOnları kaçmayı unutma `"$scriptPath`"


23
2018-01-23 03:42



Seçenekler için teşekkürler, başka bir soruda bu yanıtın sağladığı ek seçeneği gerçekten çok beğeniyorum. stackoverflow.com/a/33206405/3794873 - dragon788


Burada, bir PS komut dosyasından başka bir PS komutunu aramakla ilgili daha genel bir soruya cevap olarak, çok küçük, dar amaçlı komut dosyalarının betiklerini oluşturuyorsanız yapabileceğiniz bir cevap.

Ben sadece nokta kaynak kullanma bir durum olduğunu buldum. Yani, sadece yap:

# This is Script-A.ps1

. ./Script-B.ps1 -SomeObject $variableFromScriptA -SomeOtherParam 1234;

Ben tüm Q / A çok kafa karıştırıcı ve karmaşık buldum ve nihayetinde basit bir yönteme daha çok inmiştim, ki bu tıpkı orijinal senaryoda bir işlevmiş gibi, daha sezgisel olarak göründüğümden başka bir betik çağırmak gibi.

Dot-sourcing, aşağıdakileri kullanarak, diğer betiği bütünüyle "içe aktarabilir":

. ./Script-B.ps1

Şimdi iki dosya birleştirilmiş gibi.

Nihayetinde, gerçekten eksik olan şey, yeniden kullanılabilir fonksiyonların bir modülünü oluşturmam gerektiği fikridir.


6
2017-11-26 15:32





Kullanabiliriz splatting bunun için:

& $command @args

nerede @args (otomatik değişken $ args) bir dizi parametreye yayılır.

PS altında, 5.1


3
2017-08-19 14:06





Invoke-Expression cmdlet'ini kullanarak kabul edilen çözümü denedim ama benim için çalışmadı çünkü argümanlarım üzerinde boşluklar vardı. Argümanları ayrıştırmaya ve boşluklardan kaçmaya çalıştım ama düzgün çalışmayı başaramadım ve aynı zamanda benim düşünceme göre gerçekten kirli bir işti. Bazı deneyler yaptıktan sonra, benim sorunum şu:

function Invoke-Script
{
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $Script,

        [Parameter(Mandatory = $false)]
        [object[]]
        $ArgumentList
    )

    $ScriptBlock = [Scriptblock]::Create((Get-Content $Script -Raw))
    Invoke-Command -NoNewScope -ArgumentList $ArgumentList -ScriptBlock $ScriptBlock -Verbose
}

# example usage
Invoke-Script $scriptPath $argumentList

Bu çözümün tek dezavantajı, komut dosyanızın bir "Script" veya "ArgumentList" parametresinin bulunmadığından emin olmanız gerektiğidir.


2
2017-11-21 11:17





SQL sorgusu ile aynı şekilde çalıştırabilirsiniz. önce, komutunuzu / İfadenizi oluşturun ve bir değişkendeki saklayın ve çalıştırın / yürütün.

$command =  ".\yourExternalScriptFile.ps1" + " -param1 '$paramValue'"

Oldukça ileri, açıklamalara ihtiyacı olduğunu düşünmüyorum. Yani hepiniz emrinizi yürütmek için

Invoke-Expression $command

Buradaki istisnayı yakalamanızı tavsiye ederim


0
2017-08-03 10:19