【问题标题】:Is there any method to detect whether STDIN has been redirected within VBscript?有什么方法可以检测 STDIN 是否在 VBscript 中被重定向?
【发布时间】:2014-10-31 21:37:05
【问题描述】:

我正在尝试在 VBscript 中处理/过滤输入,但如果输入已通过管道传输到脚本中。我不希望脚本处理用户/键盘输入。我想把它编码成这样:

stdin_is_tty = ...
if not stdin_is_tty then
   ...
   input = WScript.StdIn.ReadAll
end if

否则,脚本将挂起,在执行 WScript.StdIn.ReadAll 时等待用户输入(如果我使用 WScript.StdIn.AtEndOfStream 测试流,甚至更早)。

在 C# 中,我会使用:

stdin_is_tty = not System.Console.IsInputRedirected // NET 4.5+

Q: "How to detect if Console.In (stdin) has been redirected?"accepted answer 显示了如何使用 Win32 调用通过 P/Invoke 构建该结果,用于 NET 4.5 之前的 NET 版本。但我不知道有什么方法可以将该方法转换为 VBscript。

我构建了一个笨拙的部分解决方案,使用SendKeys 将流结束序列发送到脚本的键盘缓冲区。但是如果 STDIN is 被重定向,解决方案会将键留在缓冲区中,除非我知道 STDIN 被重定向,否则我无法清理...所以,同样的问题。

我希望将脚本保存在一个打包的部分中,因此我宁愿避免使用单独的包装脚本或在通用 Windows 7+ 安装中不可用的任何内容。

有什么绝妙的想法或解决方法吗?

编辑:添加了初始解决方案的副本

我在此处添加了我改进的初始解决方案的副本(诚然,“hack”),它现在会自行清理,但仍有一些负面影响:

input = ""
stdin_is_tty = False
test_string_length = 5 ' arbitrary N (coder determined to minimize collision with possible inputs)
sendkey_string = ""
test_string = ""
for i = 1 to test_string_size
   sendkey_string = sendkey_string & "{TAB}"
   test_string = test_string & CHR(9)
next
sendkey_string = sendkey_string & "{ENTER}"
wsh.sendkeys sendkey_string ' send keyboard string signal to self
set stdin = WScript.StdIn
do while not stdin.AtEndOfStream
   input = input & stdin.ReadLine
   if input = test_string then
       stdin_is_tty = True
   else
       input = input & stdin.ReadAll
   end if
   exit do
loop
stdin.Close
if not stdin_is_tty then
   set stdin = fso.OpenTextFile( "CON:", 1 )
   text = stdin.ReadLine
   stdin.Close
end if

这个解决方案存在三个问题:

  1. 在命令行留下可见的痕迹(虽然现在,只是一个低可见性的空白行)

  2. 测试字符串(一组 N [编码器确定] TAB 后跟 NEWLINE)与任何重定向输入的第一行可能发生冲突,从而导致误报重定向确定。由于可以修改TAB的数量,因此编码器可以将这种可能性任意降低。

  3. 如果另一个窗口在执行SendKeys 部分之前获得焦点,则错误的窗口将接收代码字符串,从而导致错误的否定重定向确定。我估计这种情况发生的可能性很低。

【问题讨论】:

    标签: vbscript stdin io-redirection


    【解决方案1】:

    简而言之,不,但是...

    我已经测试了我能想到的所有东西,但还没有找到合理的方法。

    使用WScript.StdInfso.GetStdStream 检索到的TextStream 包装器所公开的属性/方法都没有提供足够的信息来确定输入是否被重定向/管道。

    尝试从衍生进程的行为/环境中获取信息(如何创建可执行文件是另一回事)也不太可能有用,因为

    • WshShell.Execute 始终生成进程并重定向其输入和输出句柄

    • WshShell.Run 创建一个不继承当前进程句柄的新进程

    • Shell.Application.ShellExecuteWshShell.Run有同样的问题

    因此,这些方法都不允许衍生进程继承当前进程的句柄以检查它们是否被重定向。

    使用 WMI 从正在运行的进程中检索信息不会返回任何可用的信息(嗯,当有重定向时,进程的HandleCount 属性会有所不同,但它并不可靠)

    因此,无法从 vbs 代码中确定是否存在重定向,剩下的选项是

    1. 不要检测到它:如果管道输入必须存在,则表现为more 命令并在所有情况下尝试检索它

    2. 指出:如果管道输入并非总是需要,请使用参数来确定是否需要读取标准输入流。

    在我的例子中,我通常使用单个斜杠 / 作为参数(为了与一些 findstr 参数保持一致,这些参数也使用斜杠来表示标准输入)。然后在vbs代码中

    If WScript.Arguments.Named.Exists("") Then 
        ' here the stdin read part
    End If
    
    1. Check before:在启动脚本之前确定是否有重定向。需要一个包装器 .cmd,但通过一些技巧,两个文件(.cmd.vbs)可以合并为一个

    保存为.cmd

    <?xml : version="1.0" encoding="UTF-8" ?> ^<!------------------------- cmd ----
    @echo off
        setlocal enableextensions disabledelayedexpansion
        timeout 1 >nul 2>nul && set "arg=" || set "arg=/"
        endlocal & cscript //nologo "%~f0?.wsf" //job:mainJob %arg% %*
        exit /b
    ---------------------------------------------------------------------- wsf --->
    <package>
      <job id="mainJob">
        <script language="VBScript"><![CDATA[
            If WScript.Arguments.Named.Exists("") Then
                Do Until WScript.StdIn.AtEndOfStream
                    WScript.StdOut.WriteLine WScript.StdIn.ReadLine
                Loop
            Else
                WScript.StdOut.WriteLine "Input is not redirected"
            End If
        ]]></script>
      </job>
    </package>
    

    这是一个.wsf 文件,存储在.cmd 中。批处理部分确定输入是否被重定向(timeout 命令无法获取重定向输入的控制台句柄)并将参数传递给脚本部分。

    然后,进程可以被调用为

    < inputfile.txt scriptwrapper.cmd             input redirected
    type inputfile.txt | scriptwrapper.cmd        input piped
    scriptwapper.cmd                              no redirection
    

    虽然这是一种方便的处理方式,但从 .cmd 调用 .wsf 部分虽然稳定且工作正常,但依赖于脚本主机/cmd 组合的未记录行为。

    当然,您也可以这样做,但要使用两个单独的文件。不那么干净,但行为已记录在案。

    【讨论】:

    • 感谢您的调查工作。你很好地描述了这种情况,并提供了一个合理的替代方案。我决定使用SendKeys 对我的初始hack 进行简化和改进的版本,这样它只会在屏幕上留下一行额外的行作为测试的证据。我认为最好的方法是将我的解决方案添加到问题中并将您的答案标记为正确。
    猜你喜欢
    • 2011-03-28
    • 1970-01-01
    • 2010-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    • 2010-10-28
    • 2016-12-01
    相关资源
    最近更新 更多