Is there any method for determining if STDIN has been redirected to VBscript?
I am trying to process / filter input in VBscript, but only if the input was submitted to a script. I don't want the script to handle user / keyboard input. I would like to code it something like this:
stdin_is_tty = ... if not stdin_is_tty then ... input = WScript.StdIn.ReadAll end if
Otherwise, the script will hang waiting for user input when it executes WScript.StdIn.ReadAll
(or even earlier if I'm testing a stream with WScript.StdIn.AtEndOfStream
).
In C #, I would use:
stdin_is_tty = not System.Console.IsInputRedirected // NET 4.5+
The accepted answer for Q is: "How to determine if Console.In (stdin) has been redirected?" shows how to plot this result using Win32 calls via P / Invoke for NET versions earlier than NET 4.5. But I don't know how this method can be translated into VBscript.
I built a clumsy, partial solution using SendKeys
to send the end-of-stream sequence to the script keyboard buffer. But the solution leaves the keys in the buffer if the STDIN is redirected and I can't clear it unless I know the STDIN was redirected ... so the same problem.
I would rather keep the script in one packaged part, so I would rather avoid having the script separately packaged or anything not available in a generic Windows 7+ installation.
Any brilliant ideas or workarounds?
EDIT: Added copy of original solution
I've added a copy of my improved original solution here (admittedly a "hack") that now clears up after itself but still has a few negatives:
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
This solution has three problems:
-
leaving visible traceback on the command line (although now only one empty line with low visibility)
-
a possible collision of a test line (a given series of N [encoder defined] TAB followed by NEWLINE) with the first line of any redirected input causing a false positive redirection definition. Since the number of TABs can be changed, this capability can be made as small as desired for the encoder.
-
a race condition that if another window gains focus before a portion is executed
SendKeys
, the wrong window will receive a line of code, resulting in a false negative redirection being detected. My assessment is that the possibility of this happening is very low.
source to share
In short, no, but ...
I've tested everything I can think of and haven't found a sane way to do this.
None of the properties / methods exposed by TextStream handlers retrieved with WScript.StdIn
or fso.GetStdStream
provide enough information to determine if the input is being redirected or not.
Trying to get information from the behavior / environment of the created process (how to create an executable is a different story) is also unlikely to be helpful, because
-
WshShell.Execute
always spawns a process with redirected input and output addresses -
WshShell.Run
creates a new process that does not inherit the descriptors of the current one -
Shell.Application.ShellExecute
has the same problem asWshShell.Run
Thus, none of these methods allow the child process to inherit the handles of the current process to check if they are redirected or not.
Using WMI to retrieve information from the current process returns nothing useful (well, the property HandleCount
for the process is different when redirected, but it is not reliable)
So, not being able to determine from the vbs code if there is a redirect, the rest of the parameters
-
Don't detect it . If the input channel must be present, act as a command
more
and in all cases try to restore it -
Please indicate . If an input signal is not always needed, use an argument to determine whether to read the stdin stream.
In my case, I usually use a single slash argument /
as an argument (for consistency with some arguments findstr
that also use a slash to indicate stdin input). Then in the vbs code
If WScript.Arguments.Named.Exists("") Then
' here the stdin read part
End If
- Check before . Determine if there is a redirect before running the script. Requires shell
.cmd
, but with a few tricks of the two files (.cmd
and.vbs
) can be combined into one
To save it as .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>
It is a .wsf
file stored internally .cmd
. The batch part determines if the input is redirected (the command timeout
cannot get the console handle on the redirected input) and pass the argument to the script part.
The process can then be called like
< inputfile.txt scriptwrapper.cmd input redirected
type inputfile.txt | scriptwrapper.cmd input piped
scriptwapper.cmd no redirection
While this is a convenient way to handle this, calling .wsf
some of them .cmd
, while stable and working without problems, relies on the undocumented behavior of the script host / cmd combination.
You can of course do the same, but with two separate files. Not that clean, but the behavior is documented.
source to share