吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 920|回复: 2
收起左侧

[其他原创] PowerShell脚本中实现限时读取用户输入

[复制链接]
TopUser 发表于 2023-7-15 12:25
本帖最后由 TopUser 于 2023-7-15 12:27 编辑

又是很久没有发帖了,突然想到之前倒腾PowerShell的时候实现了一个限时读取用户输入的函数,那就再一个帖子吧。

问题描述

在PowerShell脚本编写的时候,我们会使用Read-Host函数来接收用户的输入,但是很多时候我们想要在这个函数上附加一个超时效果,即等待用户在一段时间输入内容,如果超过时间脚本便不再等待。

解决方案概述

由于PowerShell可以调用C#代码,所以如果有C#的大佬的话,可能会有一些其他的实现思路,同时也欢迎分享。由于我对于C#了解不多,所以还是从PowerShell的角度来实现。我的实现方式我自认为具有以下特点:

  • 支持Window PowerShell和.Net PowerShell

    PowerShell有Window PowerShell版本,也就是Window自带的PowerShell。还有一种可以跨平台的.Net Core版本,官方仓库在这里Window PowerShell基本上都没有怎么维护了,所以现在的Window 10/11自带的都是Window PowerShell的5.1版本。开发时使用的是Widnow PowerShell版本,然后写好之后对于.Net Core版本做了一个适配。

  • 性能不是很好

    本函数为了做到超时终端读取效果,会开启一个进程来读取用户输入,超时再杀死进程,所以性能不高。之前我也试过一些底层的控制IO输入输入的C++接口,使用下来有一些Bug,所以最终才使用这种方式实现。

  • 效果完美(自认为)

    用户如果在限定时间输入,结果就会返回用户输入,如果超时会有提示,同时返回NULL。这个逻辑在调试的时候依旧不存在任何异常情况(这里直接指出是因为之前用了一个C#的方案,在调试的时候会出现一个Bug)。当然,你也可以在理解原理之后自行根据需求修改

源码及分析

PowerShell生态本身就不是很好,如果你只是想要在PowerShell中实现这个效果而已,那你完全可以拷贝这个函数直接用即可,不用花时间分析原理。

Function Read-HostWithTimeout {
    param(
        [string]$Prompt = "请输入内容: ",
        [System.ConsoleColor]$PromptBackGroundColor = $Host.UI.RawUI.BackgroundColor,
        [System.ConsoleColor]$PromptForeGroundColor = $Host.UI.RawUI.ForegroundColor,
        [int]$Timeout = 5000,
        [string]$TimeoutHint = "输入超时",
        [System.ConsoleColor]$HintBackgroundColor = $Host.UI.RawUI.BackgroundColor,
        [System.ConsoleColor]$HintForeGroundColor = [System.ConsoleColor]::Yellow
    )
    # 在这里调用输出提示的内容而不是在新进程里面提示是因为新进程提示的内容会被计入返回值,这里就不会
    Write-Host $Prompt -ForegroundColor $PromptForeGroundColor -BackgroundColor $PromptBackGroundColor -NoNewline

    $res = try {
        # Windows PowerShell会在超时结束抛出异常,所以这里需要定义异常可捕捉
        $ErrorActionPreference = 'Stop'
        # 新开线程执行超时读取的逻辑,注意,这里跟了一个timeout的参数,这个参数内部可以读取到
        powershell.exe -Command {
            param($Timeout)
            # 在这个进程里面新开一个Powershell线程,线程之间共享控制台对象
            $InitialSessionState = [initialsessionstate]::CreateDefault()
            $InitialSessionState.Variables.Add(
                [System.Management.Automation.Runspaces.SessionStateVariableEntry]::new(
                    "ThreadContext",
                    @{Host = $Host },
                    "share host between threads"
                )
            )
            $PSThread = [powershell]::Create($InitialSessionState)
            $null = $PSThread.AddScript{
                $ThreadContext.Host.UI.ReadLine() # 执行读取控制台操作
            }
            # 开始异步执行创建好的对象,并等待timeout秒无结果之后杀死本进程
            $Job = $PSThread.BeginInvoke()
            if (-not $Job.AsyncWaitHandle.WaitOne($Timeout)) {
                Get-Process -Id $PID | Stop-Process
            }
            else {
                return $PSThread.EndInvoke($Job)
            }
        } -args $Timeout
    }
    catch {
        # Windows PowerShell超时会执行这里的代码
        Write-Host $TimeoutHint -ForegroundColor $HintForeGroundColor -BackgroundColor $HintBackGroundColor
    }
    # .Net Core PowerShell超时会执行这里的代码
    if($null -eq $res -and $PSVersionTable.PSEdition -eq "Core"){
        Write-Host $TimeoutHint -ForegroundColor $HintForeGroundColor -BackgroundColor $HintBackGroundColor
    }
    return $res
}

调用示例

$res = Read-HostWithTimeout -Timeout 3000
# 超时$res的值就为null
# 有输入$res的值就为用户输入
Write-Host $res

免费评分

参与人数 3吾爱币 +10 热心值 +3 收起 理由
笙若 + 1 + 1 谢谢@Thanks!
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
三滑稽甲苯 + 2 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

eaglexiong 发表于 2023-7-16 19:38
看到代码很精简,支持
Holmewei 发表于 2023-7-17 11:00
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 22:13

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表