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


208

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

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

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

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

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

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

これを達成するためのクリーンな方法はありますか?私は贅沢で醜い回避策を考案することしかできませんでしたが、スクリプト言語の場合、これは非常に単純なはずだと感じています。

回答:


318

Invoke-Expression、別名もiex。以下は、例2と3で機能します。

iex $command

例1のように、exeが引用符で囲まれているため、一部の文字列はそのままでは実行されません。文字列の内容は、Powershellコマンドプロンプトから直接実行する方法とまったく同じであるため、これはそのまま動作します。

$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以外に特別なケースが存在することはあまりありません。


3
エイリアシングは素晴らしいです。別のマシンに移動したり、そのスクリプトを他の人に送信したりした場合、そのエイリアスはおそらくセットアップされません。PowerShell関数の完全な名前を優先します。
Doug Finke

1
@Doug:ほとんどの場合、それを行うか、組み込みのエイリアスを使用します(特に、コマンドラインでの簡潔さのため)。eval他の多くのスクリプト言語でそれが呼ばれているので、物事は冗談です。そして、これが誰かが何について知らなかったかを私が見た最初の質問ではありませんinvoke-expression。そして、OPのケースは社内スクリプトのように聞こえます。
ジョエルBファント

私はこれを試しましたが、動作しません:$ command = '"C:\ Program Files \ Windows Media Player \ mplayer2.exe" "H:\ Audio \ Music \ Stevie Wonder \ Stevie Wonder-Superstition.mp3" '
ジョニーカウフマン

3
「&」または「。」を付ける必要がある場合があります。それは例えば、PowerShellのネイティブでない場合は、実際のコマンドの前に署名Invoke-Expression "& $command"
TorbjörnBergstedt

トルビョルンベルグシュテットが勝利!魔法の余分な「&」が私の問題を解決しました!結果として、私は幸せで腹を立てています。
ジョニーカウフマン

19

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

1
ああ、マイクロソフトはインターネットを壊し、リンクはもう有効ではない。
JohanBoulé18年


4

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

最後に、アンインストール文字列を実行する方法を確認するためのこの素晴らしいテンプレートに出くわしました。

$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つの引数しか持てないからです。より複雑なアンインストール文字列の場合は、結合演算子を使用できます。また、私はハッシュマップを使用しましたが、実際には、おそらく配列を使用したいと思うでしょう。

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


1

呼び出し演算子を使用する場合、引数は変数に格納された配列にすることができます。

$prog = 'c:\windows\system32\cmd.exe'
$myargs = '/c','dir','/x'
& $prog $myargs

呼び出し演算子はApplicationInfoオブジェクトでも機能します。

$prog = get-command cmd
$myargs = -split '/c dir /x'
& $prog $myargs
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.