web-dev-qa-db-ja.com

どこのようにして文字列から任意のネイティブコマンドを実行しますか?

次のシナリオで自分の必要性を表現することができます:ネイティブコマンドとして実行される文字列を受け入れる関数を書きます。

これは、あまり考えたことではありません。社内の他の場所にある他のコマンドラインユーティリティと対話して、そのまま実行するコマンドを提供している場合です。あなたはコマンドを制御しないので、あなたは入力として有効なコマンドを受け入れる必要があります。これらは私が簡単に克服することができなかった主なしゃっくりです:

  1. このコマンドは、スペースを含んだパスに存在するプログラムを実行する可能性があります。

    $command = '"C:\Program Files\TheProg\Runit.exe" Hello';
    
  2. コマンドには、スペースを含むパラメータがあります。

    $command = 'echo "hello world!"';
    
  3. このコマンドには、シングルティックとダブルティックの両方があります。

    $command = "echo `"it`'s`"";
    

これを達成するための何らかのクリーンな方法はありますか?私は贅沢で醜い回避策を考案することしかできませんでしたが、スクリプト言語の場合、これは簡単なことのようです。

187
Johnny Kauffman

Invoke-Expression。これはiexとも呼ばれます。以下はあなたの例#2と#3に働きます:

iex $command

Exeが引用符で囲まれているため、一部の文字列はそのままでは実行されません(例#1)。文字列の内容は、PowershellコマンドのPromptから直接実行する方法とまったく同じであるため、これはそのまま機能します。

$command = 'C:\somepath\someexe.exe somearg'
iex $command

ただし、exeが引用符で囲まれている場合は、この例のように、コマンドラインから実行する場合と同様に、実行するには&のヘルプが必要です。

>> &"C:\Program Files\Some Product\SomeExe.exe" "C:\some other path\file.ext"

そしてスクリプトの中で:

$command = '"C:\Program Files\Some Product\SomeExe.exe" "C:\some other path\file.ext"'
iex "& $command"

おそらく、この単純な実装のように、コマンド文字列の最初の文字が"であるかどうかを検出することで、ほとんどすべてのケースを処理できます。

function myeval($command) {
    if ($command[0] -eq '"') { iex "& $command" }
    else { iex $command }
}

しかし、別の方法で呼び出さなければならないケースがいくつかあります。その場合は、おそらく特定の例外タイプ/メッセージに対してtry{}catch{}を使用するか、コマンド文字列を調べる必要があります。

常に相対パスではなく絶対パスを受け取るのであれば、上記の2以外に、多くの特別な場合があってはいけません。

277
Joel B Fant

PowerShellを使用してシェルコマンドを実行するのが本質的にどれほど難しいかについてのMicrosoft Connectのレポートもご覧ください(皮肉なことに)。

http://connect.Microsoft.com/PowerShell/feedback/details/376207/

彼らは、PowerShellがテキストを右に解釈しようとするのをやめさせる方法として--%を使うことを提案します。

例えば:

MSBuild /t:Publish --% /p:TargetDatabaseName="MyDatabase";TargetConnectionString="Data Source=.\;Integrated Security=True" /p:SqlPublishProfilePath="Deploy.publish.xml" Database.sqlproj
16
Luke Puplett

レジストリを解析してアンインストール文字列を解析し、実行しようとしたときに、受け入れられた答えが機能しませんでした。結局、Invoke-Expressionへの呼び出しは必要なかったことがわかりました。

私はついにこれに遭遇しました Nice template アンインストール文字列の実行方法を見るため:

$path = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$app = 'MyApp'
$apps= @{}
Get-ChildItem $path | 
    Where-Object -FilterScript {$_.getvalue('DisplayName') -like $app} | 
    ForEach-Object -process {$apps.Set_Item(
        $_.getvalue('UninstallString'),
        $_.getvalue('DisplayName'))
    }

foreach ($uninstall_string in $apps.GetEnumerator()) {
    $uninstall_app, $uninstall_arg = $uninstall_string.name.split(' ')
    & $uninstall_app $uninstall_arg
}

つまり、$appは2つの引数しか持たないことがわかっている社内アプリケーションであるためです。より複雑なアンインストール文字列の場合、 join operator を使用できます。また、私はハッシュマップを使用しましたが、実際には、おそらく配列を使用したいでしょう。

また、同じアプリケーションの複数のバージョンがインストールされている場合、このアンインストーラーはそれらを一度に循環するため、MsiExec.exeが混乱するため、それもあります。

4
Droogans