Тест Powershell для неинтерактивного режима
У меня есть script, который может запускаться вручную или может выполняться запланированной задачей. Мне нужно программно определить, запущен ли я в неинтерактивном режиме (который задается при выполнении по запланированной задаче) или в обычном режиме. Я googled вокруг и лучшее, что я могу найти, это добавить параметр командной строки, но у меня нет возможности сделать это с запланированными задачами, и я не могу разумно ожидать, что пользователи добавят этот параметр при их запуске вручную.
Неинтерактивный режим задает какую-то переменную или что-то, что я могу проверить в моем script?
Изменить:
Я на самом деле непреднамеренно ответил на свой вопрос, но я оставляю его здесь для потомков.
Я запустил read-host в script, чтобы спросить у пользователя что-то и когда он работал в неинтерактивном режиме, стрела, заканчивая ошибкой. Застрял в блоке try/catch и делал вещи в зависимости от того, в каком режиме я нахожусь.
Не самая красивая структура кода, но она работает. Если кто-то еще имеет лучший способ, добавьте его!
Ответы
Ответ 1
Вы можете проверить способ вызова powershell с помощью wmi:
(gwmi win32_process | ? { $_.processname -eq "powershell.exe" }) | select commandline
Содержит:
commandline
-----------
"C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe" -noprofile -NonInteractive
Ответ 2
Мне не понравился ни один из других ответов в качестве полного решения. [Environment]::UserInteractive
сообщает, является ли пользователь интерактивным, а не конкретно, если процесс является интерактивным. Api полезен для обнаружения, если вы работаете внутри службы. Здесь мое решение для обработки обоих случаев:
function Test-IsNonInteractiveShell {
if ([Environment]::UserInteractive) {
foreach ($arg in [Environment]::GetCommandLineArgs()) {
# Test each Arg for match of abbreviated '-NonInteractive' command.
if ($arg -like '-NonI*') {
return $true
}
}
}
return $false
}
Примечание. Этот код не требует многословности и занимает 3,5 мс для запуска (в среднем), но хорошо работает для иллюстрации функциональности. У меня более сложное решение, которое работает в среднем на 2,7 мс на мой GitHub. Время выполнения оценивается с помощью Measure-Command
; производительность зависит от производительности системы.
Ответ 3
C:\> powershell -NoProfile -NoLogo -NonInteractive -Command "[Environment]::GetCommandLineArgs()"
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
-NoProfile
-NoLogo
-NonInteractive
-Command
[Environment]::GetCommandLineArgs()
Ответ 4
Внедрите два сценария, один core.ps1 будет запущен вручную, и один запланированный .ps1, который запустит core.ps1 с параметром.
Ответ 5
Я придумал шикарный порт существующего и проверенного кода на С#, который использует справедливый бит P/Invoke для определения всех угловых случаев. Этот код используется в моей PowerShell Build Script, которая координирует несколько задач сборки вокруг проектов Visual Studio.
# Some code can be better expressed in C#...
#
Add-Type @'
using System;
using System.Runtime.InteropServices;
public class Utils
{
[DllImport("kernel32.dll")]
private static extern uint GetFileType(IntPtr hFile);
[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll")]
private static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
private static extern bool IsWindowVisible(IntPtr hWnd);
public static bool IsInteractiveAndVisible
{
get
{
return Environment.UserInteractive &&
GetConsoleWindow() != IntPtr.Zero &&
IsWindowVisible(GetConsoleWindow()) &&
GetFileType(GetStdHandle(-10)) == 2 && // STD_INPUT_HANDLE is FILE_TYPE_CHAR
GetFileType(GetStdHandle(-11)) == 2 && // STD_OUTPUT_HANDLE
GetFileType(GetStdHandle(-12)) == 2; // STD_ERROR_HANDLE
}
}
}
'@
# Use the interactivity check somewhere:
if (![Utils]::IsInteractiveAndVisible)
{
return
}
Ответ 6
Тестирование интерактивности должно, вероятно, учитывать как процесс, так и пользователя. Поиск переключателя powershell -NonInteractive
(минимально -noni
) для определения интерактивности процесса (очень похож на @VertigoRay script) может быть выполнен с использованием простого фильтра с легким условием -like
:
function Test-Interactive
{
<#
.Synopsis
Determines whether both the user and process are interactive.
#>
[CmdletBinding()] Param()
[Environment]::UserInteractive -and
!([Environment]::GetCommandLineArgs() |? {$_ -ilike '-NonI*'})
}
Это позволяет избежать накладных расходов WMI, исследования процессов, императивного беспорядка, двойного отрицательного наименования и даже полного регулярного выражения.
Ответ 7
Я хотел бы разместить здесь обновленный ответ, потому что кажется, что [Environment]::UserInteractive
не ведет себя одинаково между .NET Core (контейнер с microsoft/nanoserver
) и .NET Full (контейнер работает microsoft/windowsservercore
).
Пока [Environment]::UserInteractive
вернет True
или False
в "обычных" Windows, он вернет $null
в "nanoserver".
Если вы хотите проверить интерактивный режим независимо от значения, добавьте эту проверку в свой script:
($null -eq [Environment]::UserInteractive -or [Environment]::UserInteractive)
Ответ 8
powerShell -NonInteractive { Get-WmiObject Win32_Process -Filter "Name like '%powershell%'" | select-Object CommandLine }
powershell -Command { Get-WmiObject Win32_Process -Filter "Name like '%powershell%'" | select-Object CommandLine }
В первом случае вы получите параметр "-NonInteractive". В последнем вы не будете.
Ответ 9
Script: IsNonInteractive.ps1
function Test-IsNonInteractive()
{
#ref: http://www.powershellmagazine.com/2013/05/13/pstip-detecting-if-the-console-is-in-interactive-mode/
#powershell -NoProfile -NoLogo -NonInteractive -File .\IsNonInteractive.ps1
return [bool]([Environment]::GetCommandLineArgs() -Contains '-NonInteractive')
}
Test-IsNonInteractive
Пример использования (из командной строки)
pushd c:\My\Powershell\Scripts\Directory
::run in non-interactive mode
powershell -NoProfile -NoLogo -NonInteractive -File .\IsNonInteractive.ps1
::run in interactive mode
powershell -File .\IsNonInteractive.ps1
popd
Более востребованный пример Powershell Script
#script options
$promptForCredentialsInInteractive = $true
#script starts here
function Test-IsNonInteractive()
{
#ref: http://www.powershellmagazine.com/2013/05/13/pstip-detecting-if-the-console-is-in-interactive-mode/
#powershell -NoProfile -NoLogo -NonInteractive -File .\IsNonInteractive.ps1
return [bool]([Environment]::GetCommandLineArgs() -Contains '-NonInteractive')
}
function Get-CurrentUserCredentials()
{
return [System.Net.CredentialCache]::DefaultCredentials
}
function Get-CurrentUserName()
{
return ("{0}\{1}" -f $env:USERDOMAIN,$env:USERNAME)
}
$cred = $null
$user = Get-CurrentUserName
if (Test-IsNonInteractive)
{
$msg = 'non interactive'
$cred = Get-CurrentUserCredentials
}
else
{
$msg = 'interactive'
if ($promptForCredentialsInInteractive)
{
$cred = (get-credential -UserName $user -Message "Please enter the credentials you wish this script to use when accessing network resources")
$user = $cred.UserName
}
else
{
$cred = Get-CurrentUserCredentials
}
}
$msg = ("Running as user '{0}' in '{1}' mode" -f $user,$msg)
write-output $msg