Подтвердить что ты не робот

Тест Powershell для неинтерактивного режима

У меня есть script, который может запускаться вручную или может выполняться запланированной задачей. Мне нужно программно определить, запущен ли я в неинтерактивном режиме (который задается при выполнении по запланированной задаче) или в обычном режиме. Я googled вокруг и лучшее, что я могу найти, это добавить параметр командной строки, но у меня нет возможности сделать это с запланированными задачами, и я не могу разумно ожидать, что пользователи добавят этот параметр при их запуске вручную. Неинтерактивный режим задает какую-то переменную или что-то, что я могу проверить в моем script?

Изменить: Я на самом деле непреднамеренно ответил на свой вопрос, но я оставляю его здесь для потомков.

Я запустил read-host в script, чтобы спросить у пользователя что-то и когда он работал в неинтерактивном режиме, стрела, заканчивая ошибкой. Застрял в блоке try/catch и делал вещи в зависимости от того, в каком режиме я нахожусь.

Не самая красивая структура кода, но она работает. Если кто-то еще имеет лучший способ, добавьте его!

4b9b3361

Ответ 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