Правильное использование Invoke-Expression?
Недавно я завершил свою первую ночную сборку script (первое значительное что-либо script, действительно) в powershell. Кажется, что у меня все хорошо работает, если еще не уверенно (я еще не справился с серьезной проверкой ошибок), но я обнаружил, что попадаю в идиому вокруг командлета Invoke-Expression
, и мне интересно, использую ли я это правильно.
В частности, я использую ряд переменных для создания командных строк, которые я буду использовать для построения решения, а затем запускайте тестовые модули решения. например, что-то вроде:
$tmpDir = "C:\Users\<myuser>\Development\Autobuild"
$solutionPath=$tmpDir+"\MyProj\MyProj.sln"
$devenv="C:\Program Files (x86)\Microsoft Visual Studio 10.0\common7\ide\devenv"
$releaseProfile="Release"
$releaseCommandLine="`"$devenv`" `"$solutionPath`" /build `"$releaseProfile`""
Это работает достаточно хорошо, $releaseCommandLine
содержит командную строку, которую я хочу выполнить, когда закончите. Затем я выполняю его по этой строке:
$output = Invoke-Expression "& $releaseCommandLine"
Правильно ли это выполнить ручную командную строку из powershell script? Сначала я думал, что Invoke-Command
сделает это, но я, должно быть, делал что-то неправильно, потому что я не мог получить эту работу вообще в течение получаса, и я почти сразу начал работать.
Я повторил этот же шаблон несколько раз в этом же script. Является ли это лучшей практикой?
Ответы
Ответ 1
Выглядит хорошо. Единственное, что я изменил, - это использовать больше возможностей Powershell вместо хрупких предположений. Например:.
-
использовать Join-Path вместо конкатенации строк
-
используйте поставщик Env:\
для поиска директории %programfiles(x86)%
(или, еще лучше, используйте HKML:\
поставщик, чтобы найти путь - это в ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ \Microsoft\VisualStudio\\InstallDir)
-
когда мне нужно написать строку, содержащую буквенные двойные значения и, я обычно возвращаюсь к синтаксису ниже. Личное предпочтение, очевидно.
'"{0}" "{1}" /build "{2}"' -f $devenv, $solutionPath, $releaseProfile
В некоторых случаях я был бы склонен использовать Process.Start(), чтобы я мог захватывать потоки stdout и stderr независимо (и, возможно, даже контролировать stdin интерактивно, в зависимости от приложения).
PS - '&' не является строго необходимым.
Ответ 2
Я думаю, что здесь нет необходимости использовать Invoke-Expression. Я сделал это с большим количеством скриптов сборки и обычно выглядит так:
$vsroot = "$env:ProgramFiles(x86)\Microsoft Visual Studio 9.0"
$devenv = "$vsroot\Common7\IDE\devenv.exe"
$sln = Join-Path <source_root> Source\MyProj\MyProj.sln
& $devenv $sln /build Release
или
& $devenv $sln /build "Release|Any CPU"
Хотя в последнее время у меня были некоторые проблемы с использованием devenv.exe(неправильные надстройки и т.д.), поэтому теперь я использую msbuild.exe:
$msbuild = 'C:\Windows\Microsoft.NET\Framework\v3.5\MSBuild.exe'
& $msbuild $sln /p:Configuration=Release
В настоящее время MSBuild может обрабатывать С#, VB и С++ (вызывает vcbuild), но не может обрабатывать решения с проектами установки и развертывания в них. Однако я нашел, что он более надежный, чем при использовании devenv.exe.
Кстати, вам обычно нужно вызывать другие инструменты (sn.exe, signtool.exe, mt.exe и т.д.) в сборке script, которые относятся к версии Visual Studio/.NET, с которой вы хотите построить. Поэтому обычно лучше настраивать переменные среды так же, как это делает командная строка VS 2008. Установив PowerShell Community Extensions, вы можете включить одну строку в заголовке профиля PSCX, чтобы включить это для настроек .NET 3.5/VS 2008:
$Pscx:Preferences["ImportVisualStudioVars"] = $true