我正在尝试在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+
accepted answer Q: "How to detect if Console.In (stdin) has been redirected?"显示了如何通过P / Invoke使用Win32调用构建该结果,适用于早于.NET 4.5的.NET版本。但我不知道有任何方法可以将该方法转换为VBscript。
我使用SendKeys
构建了一个笨拙的部分解决方案,将流末尾序列发送到脚本的键盘缓冲区。但是如果STDIN 被重定向,解决方案会将密钥保留在缓冲区中,除非我知道STDIN被重定向,否则我无法清理...所以,同样的问题。
我更喜欢将脚本保存在一个打包的部分中,所以我宁愿避免单独的包装脚本或通用Windows 7+安装中没有的任何内容。
任何出色的想法或解决方法?
编辑:添加了初始解决方案的副本
我在这里添加了我改进的初始解决方案的副本(诚然,这是一个“黑客”),它现在已经清理完毕,但仍然有几个底片:
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
这个解决方案有三个问题:
在命令行留下可见的痕迹(虽然现在只有一条空白线,但能见度低)
测试字符串(一系列N [编码器确定的] TAB后跟NEWLINE)的可能冲突与任何重定向输入的第一行导致误报重定向确定。由于可以修改TAB的数量,编码器可以任意降低这种可能性。
竞争条件,如果另一个窗口在执行SendKeys
部分之前获得焦点,则错误的窗口将接收代码字符串,从而导致错误的否定重定向确定。我的估计是这种情况发生的可能性非常低。
答案 0 :(得分:5)
简而言之,不,但......
我已经测试了我能想到的一切,并且没有找到合理的方法来做到这一点。
使用WScript.StdIn
或fso.GetStdStream
检索的TextStream包装器公开的属性/方法都没有提供足够的信息来确定输入是否被重定向/管道传送。
尝试从衍生进程的行为/环境中获取信息(如何创建可执行文件是其他故事)也不太可能有用,因为
WshShell.Execute
总是在其输入和输出句柄重定向的情况下生成进程
WshShell.Run
创建一个不继承当前句柄的新流程
Shell.Application.ShellExecute
与WshShell.Run
因此,这些方法都不允许生成的进程继承当前进程的句柄,以检查它们是否被重定向。
使用WMI从正在运行的进程中检索信息不会返回任何可用的内容(当有重定向时,进程的HandleCount
属性会有所不同,但它不可靠)
因此,无法从vbs代码确定是否存在重定向,其余选项为
没有检测到:如果管道输入必须存在,则表现为more
命令并且在所有情况下都尝试检索它
表示:如果管道输入并非总是需要,请使用参数来确定是否需要读取stdin流。
在我的例子中,我通常使用单个斜杠/
作为参数(用于与一些findstr
参数的一致性,这些参数也使用斜杠来表示stdin输入)。然后在vbs代码中
If WScript.Arguments.Named.Exists("") Then
' here the stdin read part
End If
.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
虽然这是处理它的一种方便的方法,但.wsf
部分从.cmd
调用,虽然稳定且无问题,但仍依赖于脚本host / cmd的未记录行为组合
当然,您可以使用两个单独的文件执行相同的操作。不那么干净,但行为记录在案。