#scripting #powershell
#сценарии #powershell
Вопрос:
Я могу выразить свою потребность следующим сценарием: Напишите функцию, которая принимает строку для запуска как встроенную команду.
Идея не слишком притянута за уши: если вы взаимодействуете с другими утилитами командной строки из других подразделений компании, которые предоставляют вам команду для выполнения дословно. Поскольку вы не управляете командой, вам нужно принять любую допустимую команду в качестве входных данных. Это основные проблемы, которые я не смог легко преодолеть:
-
Команда может выполнить программу, находящуюся в пути с пробелом в нем:
$command = '"C:Program FilesTheProgRunit.exe" Hello';
-
Команда может содержать параметры с пробелами в них:
$command = 'echo "hello world!"';
-
Команда может содержать как одинарные, так и двойные такты:
$command = "echo `"it`'s`"";
Есть ли какой-нибудь чистый способ добиться этого? Я смог разработать только щедрые и уродливые обходные пути, но для языка сценариев я чувствую, что это должно быть предельно просто.
Ответ №1:
Invoke-Expression
, также обозначается как iex
. Следующее будет работать в ваших примерах # 2 и # 3:
iex $command
Некоторые строки не будут выполняться как есть, например, в вашем примере # 1, потому что exe-файл заключен в кавычки. Это будет работать как есть, потому что содержимое строки в точности соответствует тому, что вы бы запустили прямо из командной строки Powershell:
$command = 'C:somepathsomeexe.exe somearg'
iex $command
Однако, если exe-файл заключен в кавычки, вам понадобится помощь amp;
, чтобы запустить его, как в этом примере, при запуске из командной строки:
>> amp;"C:Program FilesSome ProductSomeExe.exe" "C:some other pathfile.ext"
И затем в сценарии:
$command = '"C:Program FilesSome ProductSomeExe.exe" "C:some other pathfile.ext"'
iex "amp; $command"
Вероятно, вы могли бы обработать почти все случаи, определив, является ли первый символ командной строки "
, как в этой наивной реализации:
function myeval($command) {
if ($command[0] -eq '"') { iex "amp; $command" }
else { iex $command }
}
Но вы можете обнаружить некоторые другие случаи, которые должны быть вызваны другим способом. В этом случае вам нужно будет либо использовать try{}catch{}
, возможно, для определенных типов исключений / сообщений, либо изучить командную строку.
Если вы всегда получаете абсолютные пути вместо относительных путей, у вас не должно быть много особых случаев, если таковые имеются, за пределами 2 выше.
Комментарии:
1. Наложение псевдонимов — это здорово. Помните, что если вы перейдете на другую машину или отправите этот скрипт кому-то другому, этот псевдоним, вероятно, не будет настроен. Предпочитайте полные имена функций PowerShell.
2. @Doug: Большую часть времени я делаю это или использую встроенные псевдонимы (особенно для краткости в командной строке).
eval
Это полушутя, потому что так это называется во многих других языках сценариев, и это не первый вопрос, который я видел, о котором кто-то понятия не имелinvoke-expression
. И случай OP звучит как только внутренний скрипт.3. Я пробовал это, но это не работает с: $command = ‘»C:Program Файлы Windows Media Playermplayer2.exe » «H:AudioMusicStevie Wonder Стиви Уандер — Суеверие.mp3″‘
4. Возможно, вам придется поставить знак «amp;» или «.» перед фактической командой, если она не является встроенной для powershell, например
Invoke-Expression "amp; $command"
5. Я не смог заставить вышеуказанное работать для меня так, как мне было нужно. В итоге я использовал amp; cmd / c $ command, которая не требовала изменения кавычек в $ command
Ответ №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
Комментарии:
1. Ах, и Microsoft отключила Интернет, и ссылка больше не действительна.
2. Приведенная выше ссылка включена archive.org в web.archive.org/web/20131122050220/http ://…
3. Мне нравится эта статья. Полностью подводит итог тому, что «pwsh» не является оболочкой.
Ответ №3:
Если вы хотите использовать оператор вызова, аргументами может быть массив, хранящийся в переменной:
$prog = 'c:windowssystem32cmd.exe'
$myargs = '/c','dir','/x'
amp; $prog $myargs
Оператор вызова также работает с объектами ApplicationInfo.
$prog = get-command cmd
$myargs = -split '/c dir /x'
amp; $prog $myargs
Комментарии:
1. @YoraiLevi режим аргументации против режима выражения
amp; $wsl (-split "--install -d Ubuntu-20.04")
Ответ №4:
Принятый ответ не сработал у меня при попытке проанализировать реестр на наличие строк удаления и выполнить их. Оказывается, мне не нужен был вызов Invoke-Expression
в конце концов.
Я, наконец, наткнулся на этот хороший шаблон, позволяющий увидеть, как выполнять строки удаления:
$path = 'HKLM:SOFTWAREWow6432NodeMicrosoftWindowsCurrentVersionUninstall'
$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(' ')
amp; $uninstall_app $uninstall_arg
}
Это работает для меня, а именно потому, что $app
это внутреннее приложение, которое, как я знаю, будет иметь только два аргумента. Для более сложных строк удаления вы можете захотеть использовать оператор объединения. Кроме того, я просто использовал хэш-карту, но на самом деле, вы, вероятно, захотите использовать массив.
Кроме того, если у вас установлено несколько версий одного и того же приложения, этот деинсталлятор будет перебирать их все одновременно, что сбивает с толку MsiExec.exe
, так что это тоже возможно.
Комментарии:
1. Предполагается, что путь к удалению не содержит пробелов.