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.

+3


source to share


1 answer


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 as WshShell.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

      

  1. 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.

+5


source







All Articles