Правильное использование 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