Как вы выполняете произвольную нативную команду из строки?
Я могу выразить свою потребность в следующем сценарии: Напишите функцию, которая принимает строку для запуска в качестве собственной команды.
Идея не слишком далека: если вы взаимодействуете с другими утилитами командной строки из других частей компании, которые предоставляют вам команду для дословного запуска. Поскольку вы не управляете командой, вам необходимо принять любую допустимую команду в качестве ввода. Вот основные икоты, которые я не смог легко преодолеть:
Команда может выполнить программу, находящуюся в пути с пробелом в ней:
$command = '"C:\Program Files\TheProg\Runit.exe" Hello';
Команда может иметь параметры с пробелами в них:
$command = 'echo "hello world!"';
Команда может иметь одинарные и двойные галочки:
$command = "echo `"it`'s`"";
Есть ли какой-нибудь чистый способ сделать это? Я только смог придумать обильные и безобразные обходные пути, но для языка сценариев я чувствую, что это должно быть очень просто.
3 ответа
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 выше.
Пожалуйста, ознакомьтесь также с отчетом Microsoft Connect о том, как тяжело использовать PowerShell для запуска команд оболочки (о, ирония судьбы).
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
Если вы хотите использовать оператор вызова, аргументы могут быть массивом, хранящимся в переменной:
$prog = 'c:\windows\system32\cmd.exe'
$myargs = '/c','dir','/x'
& $prog $myargs
Оператор вызова также работает с объектами ApplicationInfo.
$prog = get-command cmd
$myargs = -split '/c dir /x'
& $prog $myargs
Принятый ответ не работал для меня, когда я пытаюсь проанализировать реестр на наличие строк удаления и выполнить их. Оказывается, мне не нужно было звонить 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
это внутреннее приложение, которое, как я знаю, будет иметь только два аргумента. Для более сложных строк удаления вы можете использовать оператор объединения. Кроме того, я просто использовал хеш-карту, но на самом деле вы, вероятно, захотите использовать массив.
Кроме того, если у вас установлено несколько версий одного и того же приложения, этот деинсталлятор будет циклически повторять их все одновременно, что приводит к путанице MsiExec.exe
так что это тоже.