非交互模式的Powershell测试

时间:2012-03-16 13:54:53

标签: powershell

我有一个可以手动运行的脚本,也可以由计划任务运行。我需要以编程方式确定我是否在非交互模式(通过计划任务运行时设置)或正常模式运行。我已经google了,我能找到的最好的是添加命令行参数,但我没有任何可行的方法来执行计划任务,也不能合理地期望用户在运行时添加参数手动。 非交互模式是否设置了某种变量或我可以在我的脚本中检查的内容?

编辑: 我实际上无意中回答了我自己的问题,但是我将它留在这里作为后代。

我在脚本中插入了一个读取主机,要求用户提供一些东西,当它以非交互模式运行时,繁荣,终止错误。把它放在try / catch块中,根据我所处的模式做事。

不是最漂亮的代码结构,但它有效。如果其他人有更好的方法请添加它!

11 个答案:

答案 0 :(得分:21)

我不喜欢任何其他答案作为一个完整的解决方案。 [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毫秒(平均而言),但可以很好地说明功能。我有一个更简洁的解决方案,在my GitHub上以2.7毫秒(平均)运行。 使用Measure-Command评估的执行时间;性能取决于系统性能。

答案 1 :(得分:14)

您可以使用wmi检查powershell的调用方式:

 (gwmi win32_process | ? { $_.processname -eq "powershell.exe" }) | select commandline

提供:

commandline
-----------

"C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe"  -noprofile -NonInteractive

答案 2 :(得分:2)

我想在这里提供一个更新的答案,因为似乎[Environment]::UserInteractive在.NET Core(运行microsoft/nanoserver的容器)和.NET Full(容器运行{{1}之间的行为不一样}})。

虽然microsoft/windowsservercore会在“常规”Windows中返回[Environment]::UserInteractiveTrue,但它会在'nanoserver'中返回False

如果您想要检查交互模式而不考虑值,请将此检查添加到您的脚本中:

$null

编辑:要回答为何不仅仅检查真实性的评论,请考虑以下真值表:

($null -eq [Environment]::UserInteractive -or [Environment]::UserInteractive)

答案 3 :(得分:1)

C:\> powershell -NoProfile -NoLogo -NonInteractive -Command "[Environment]::GetCommandLineArgs()"
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
-NoProfile
-NoLogo
-NonInteractive
-Command
[Environment]::GetCommandLineArgs()

答案 4 :(得分:1)

我想出了一个现有且经过验证的C#代码的豪华端口,该代码使用相当多的P / Invoke来确定所有极端情况。此代码用于PowerShell Build Script,用于协调围绕Visual Studio项目的几个构建任务。

{{1}}

答案 5 :(得分:1)

测试交互性应该考虑到流程和用户。寻找-NonInteractive(最小-noni)powershell开关来确定进程交互性(非常类似于@ VertigoRay的脚本)可以使用具有轻量级-like条件的简单过滤器来完成:

function Test-Interactive
{
    <#
    .Synopsis
        Determines whether both the user and process are interactive.
    #>

    [CmdletBinding()] Param()
    [Environment]::UserInteractive -and
        !([Environment]::GetCommandLineArgs() |? {$_ -ilike '-NonI*'})
}

这避免了WMI的开销,进程探索,命令性混乱,双重否定命名,甚至是完整的正则表达式。

答案 6 :(得分:1)

我认为这个问题需要更彻底的评估。

  • “交互式”表示外壳程序以REPL运行-连续的读取-执行-打印循环。

  • “非交互式”表示外壳程序正在执行脚本,命令或脚本块,并且在执行后终止

如果PowerShell在运行时使用任何选项-Command-EncodedCommand-File,则它是非交互式的。不幸的是,您还可以运行没有 选项(pwsh script.ps1)的脚本,因此没有防弹方法来检测外壳是否是交互式的。

那我们是不是很幸运?否,幸运的是,PowerShell确实会自动添加选项,以便我们测试PowerShell是否运行脚本块或通过ssh运行以执行命令(ssh user@host command)。

function IsInteractive {
    # not including `-NonInteractive` since it apparently does nothing
    # "Does not present an interactive prompt to the user" - no, it does present!
    $non_interactive = '-command', '-c', '-encodedcommand', '-e', '-ec', '-file', '-f'

    # alternatively `$non_interactive [-contains|-eq] $PSItem`
    -not ([Environment]::GetCommandLineArgs() | Where-Object -FilterScript {$PSItem -in $non_interactive})
}

现在在PowerShell配置文件中测试它是否处于交互模式,因此在执行脚本,命令或脚本块时该配置文件不会运行(您仍然必须记住运行pwsh -f script.ps1-而不是{{1 }})

pwsh script.ps1

答案 7 :(得分:0)

实现两个脚本,一个core.ps1要手动启动,一个scheduled.ps1用一个参数启动core.ps1。

答案 8 :(得分:0)

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 }

在第一种情况下,您将获得&#34; -NonInteractive&#34; PARAM。在后者中你赢了。

答案 9 :(得分:0)

脚本: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 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

答案 10 :(得分:0)

使用-Noninteractive开关启动PowerShell提示符时,它将返回一个布尔值。

[Environment]::GetCommandLineArgs().Contains('-NonInteractive')