74

2番目のPowerShellスクリプトの関数に引数として渡したい名前と値のペアのセットを生成する構成ファイルを読み取るスクリプトがあります。

設計時にこの構成ファイルに配置されるパラメーターがわからないため、この2番目のPowerShellスクリプトを呼び出す必要がある時点で、基本的に、この2番目のスクリプトへのパスを持つ変数が1つと、2番目の変数があります。パス変数で識別されるスクリプトに渡す引数の配列である変数。

したがって、2番目のスクリプト($scriptPath)へのパスを含む変数は、次のような値を持つ可能性があります。

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

引数($argumentList)を含む変数は次のようになります。

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

この状況から、$ argumentsListのすべての引数を使用してscript.ps1を実行するにはどうすればよいですか?

この2番目のスクリプトからのwrite-hostコマンドは、この最初のスクリプトが呼び出されるコンソールに表示されるようにしたいと思います。

ドットソーシング、Invoke-Command、Invoke-Expression、およびStart-Jobを試しましたが、エラーが発生しないアプローチは見つかりませんでした。

たとえば、最初の最も簡単なルートは、次のように呼び出されるStart-Jobを試すことだと思いました。

Start-Job -FilePath $scriptPath -ArgumentList $argumentList

...しかし、これはこのエラーで失敗します:

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

...この場合、「ConfigFilename」は2番目のスクリプトで定義されたパラメーターリストの最初のパラメーターであり、私の呼び出しは明らかにその値を「-ConfigFilename」に設定しようとしています。これは明らかにパラメーターを名前で識別することを目的としています。 、その値を設定しません。

私は何が欠けていますか?

編集:

わかりました。これは、invoke.ps1という名前のファイルにある、呼び出されるスクリプトのモックアップです。

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

...これは、invoker.ps1という名前のファイルにある呼び出しスクリプトのモックアップです。

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

私がinvoker.ps1を実行すると、これはInvoke-Expressionの最初の呼び出しで現在発生しているエラーです。

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

2番目の呼び出しは問題なく機能しますが、1つの重要な違いは、最初のバージョンではパスにスペースが含まれる引数を使用し、2番目のバージョンでは使用しないことです。これらのパスのスペースの存在を誤って処理していますか?

4

8 に答える 8

65

あは。これは、スクリプトへのパスにスペースがあるという単純な問題であることが判明しました。

Invoke-Expression行を次のように変更します。

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

...それを開始するのに十分でした。あなたの助けとフィードバックをくれたNeoliskに感謝します!

于 2012-10-12T03:25:56.720 に答える
48

Invoke-Expression完全に機能するはずです。正しく使用していることを確認してください。あなたの場合、それはこのように見えるはずです:

Invoke-Expression "$scriptPath $argumentList"

このアプローチをGet-Serviceでテストしましたが、期待どおりに機能しているようです。

于 2012-10-12T00:52:49.267 に答える
33

実際にはもっと簡単です:

方法1:

Invoke-Expression $scriptPath $argumentList

方法2:

& $scriptPath $argumentList

方法3:

$scriptPath $argumentList

にスペースがある場合はscriptPath、それらをエスケープすることを忘れないでください`"$scriptPath`"

于 2014-01-23T03:42:03.917 に答える
13

これは、PSスクリプトから別のPSスクリプトを呼び出すという、より一般的な質問をカバーする回答です。これは、スクリプトを多くの小さな目的の狭いスクリプトで構成している場合と同じです。

単にドットソーシングを使用した場合であることがわかりました。つまり、次のことを行うだけです。

# This is Script-A.ps1

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

すべてのQ/Aが非常に混乱し、複雑で、最終的に上記の単純なメソッドにたどり着きました。これは、元のスクリプトの関数であるかのように別のスクリプトを呼び出すのとまったく同じで、より直感的に思えます。

ドットソーシングは、以下を使用して、他のスクリプト全体を「インポート」できます。

. ./Script-B.ps1

これで、2つのファイルがマージされたかのようになります。

最終的に、私が本当に欠けていたのは、再利用可能な関数のモジュールを構築する必要があるという概念です。

于 2015-11-26T15:32:50.027 に答える
13

これにはスプラッティングを使用できます。

& $command @args

ここで、@args自動変数$ args)はパラメーターの配列にスプラットされます。

PSでは、5.1

于 2017-08-19T14:06:52.817 に答える
2

Invoke-Expressionコマンドレットを使用するという一般的な解決策を試しましたが、引数にスペースが含まれていたため、うまくいきませんでした。引数を解析してスペースをエスケープしようとしましたが、正しく機能させることができず、また、私の意見では本当に汚い回避策でした。それで、いくつかの実験の後、問題に対する私の見解はこれです:

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

このソリューションの唯一の欠点は、スクリプトに「Script」または「ArgumentList」パラメーターがないことを確認する必要があることです。

于 2016-11-21T11:17:40.577 に答える
1

SQLクエリと同じように実行できます。まず、コマンド/式を作成して変数に格納し、実行/呼び出します。

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

かなり前向きで、説明は必要ないと思います。これで、コマンドを実行する準備が整いました。

Invoke-Expression $command

ここで例外をキャッチすることをお勧めします

于 2017-08-03T10:19:34.287 に答える
-1

別の.ps1ファイルから.ps1ファイル[ここでは$scriptPathと$argumentListに格納されている複数の引数]を実行するとします。

Invoke-Expression "& $scriptPath $argumentList"

このコードは正常に機能します

于 2020-03-31T08:34:39.850 に答える