Redirect STDOUT to GUI

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Redirect STDOUT to GUI

#1 Post by siberia-man » 23 Oct 2016 17:09

Introduction

You can call me insane but I did it and would like to share it with you.

The idea and first implementation have appeared few years ago. Initially I was needed to forward output of some commands directly into my favorite browser. During all this time it was improved many times (become simplified, complicated and simplified again). Now it still works properly and I don't see ways to simplify it without loosing flexibility.

There is script doing very simple work -- it captures STDOUT of some command to a temporary file and launches some GUI application with this file. in the other words, it mimics other command line tools and opens output of commands in GUI application.

Explanation
Definitely, I am slightly mad. The essential code is very simple. Because GUI is not able to read STDIN (I guess there could be such a kind of GUIs, but the majority of them cannot). So we just store output to some file and open it in some application:

Code: Select all

command > tempfile
guiapp tempfile


The rest of the script (over 260 lines in total, over 100 lines of pure code) is needed to make the script flexible and configurable as much as possible.

Examples

Running without options it always invokes notepad:

Code: Select all

echo:Hello, world | 2


Let's see the same text in an another application, for example in the default browser (usually MSIE)

Code: Select all

echo:Hello, world | 2 html


Let's see now the same text in another browser. Assume we have two browsers in the system MSIE as default one and the another one (let say, Firefox). Create the file 2.html.bat. Once we have this configuration file, we are able to change the application

Code: Select all

:: Set the extension to recognize the data type
set "pipeext=.html"

:: Mozilla Command Line Options
:: https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options
::
:: firefox -h | more
set "pipecmd="%ProgramFiles%\Firefox\firefox.exe" %%*"


If you need to change behavior of the application, you can launch it with an another extension. So the following example invokes the browser (as it is defined in 2.html.bat) and says to assume data as a plain text file (.txt extension).

Code: Select all

echo:Hello world | 2 html.txt


It supports few options:

Code: Select all

-d DIR     use DIR for storing temp files
-n NAME    use NAME as the name of temp file
--debug    turn on debug information
--dry-run  don't invoke a command, display only


Source

Source code is available in the attached file and following by the link:
https://github.com/ildar-shaimordanov/t ... /bin/2.bat
https://github.com/ildar-shaimordanov/t ... 2.html.bat

Code: Select all

::HELP Redirects output of command line tools to GUI application.
::HELP
::HELP
::HELP USAGE
::HELP
::HELP command | 2 [OPTIONS] [APP[.EXT] | EXT] [APP-OPTIONS]
::HELP
::HELP
::HELP OPTIONS
::HELP
::HELP -d DIR     use DIR for storing temp files
::HELP -n NAME    use NAME as the name of temp file
::HELP --debug    turn on debug information
::HELP --dry-run  don't invoke a command, display only
::HELP
::HELP
::HELP DESCRIPTION
::HELP
::HELP The script is flexible enough to enable many ways to invoke GUI
::HELP applications. Which GUI application would be invoked is defined by
::HELP the arguments. Depending on what is this, it will be called to
::HELP declare few specific environment variables.
::HELP
::HELP
::HELP INVOCATION
::HELP
::HELP commands | 2
::HELP
::HELP With no parameters runs Notepad. Always.
::HELP
::HELP commands | 2 APP
::HELP
::HELP "APP" is the parameter defining an application or a family of
::HELP applications or an extension (without the leading "dot" symbol).
::HELP The script looks around for the file called as "2.APP.bat". If the
::HELP file exists, invokes it to set the needful environment variables.
::HELP The script should declare few specific environment variables (see
::HELP the "ENVIRONMENT" section below).
::HELP
::HELP commands | 2 APP.EXT
::HELP
::HELP The same as above but ".EXT" overrides early declared extension.
::HELP
::HELP commands | 2 EXT
::HELP
::HELP If there is no file "2.APP.bat", the argument is assumed as the
::HELP extension (without the leading "dor" symbol), the script does
::HELP attempt to find an executable command (using "assoc" and "ftype")
::HELP and prepare invocation of the command found by these commands.
::HELP
::HELP
::HELP ENVIRONMENT
::HELP
::HELP %pipecmd%
::HELP
::HELP (Mandatory)
::HELP Invocation string for the application. It could or could not
::HELP contain additional parameters supported by the application.
::HELP
::HELP %pipeext%
::HELP
::HELP (Optional, but recommended to set)
::HELP Extenstion (like ".txt" or ".html" etc). It can be useful in the
::HELP case if the application is able to handle different data files.
::HELP
::HELP %pipetmpsave%
::HELP
::HELP (Optional)
::HELP The command line tool used for capturing output of commands and
::HELP redirecting to a resulting file. By default it is set as follows:
::HELP
::HELP set "pipetmpsave=more"
::HELP
::HELP Another possible setting is:
::HELP
::HELP set "pipetmpsave=findstr "$""
::HELP
::HELP
::HELP CONFIGURATION
::HELP
::HELP Using the file "2-settings.bat" located in the same directory
::HELP allows to configure the global environment variables of the main
::HELP script. It is good place for setting such kind of variables as
::HELP %pipetmpdir%, %pipetmpname% and %pipetmpsave%.
::HELP
::HELP
::HELP SEE ALSO
::HELP
::HELP ASSOC /?
::HELP FTYPE /?

:: ========================================================================

@echo off

timeout /t 0 >nul 2>&1 && (
   call :pipe-help
   goto :EOF
)

:: ========================================================================

setlocal

set "pipedbg="
set "pipedry="

set "pipetmpdir=%TEMP%"
set "pipetmpname=pipe.%RANDOM%"
set "pipetmpfile="
set "pipetmpsave=more"

if exist "%~dpn0-settings.bat" call "%~dpn0-settings.bat"

set "pipecmd="
set "pipecmdopts="
set "pipetitle="
set "pipeext="

:: ========================================================================

:pipe-options-begin

if "%~1" == "" goto :pipe-options-end

if "%~1" == "-d" (
   set "pipetmpdir=%~2"
   shift /1
   shift /1
) else if "%~1" == "-n" (
   set "pipetmpname=%~2"
   shift /1
   shift /1
) else if "%~1" == "--debug" (
   set "pipedbg=1"
   shift /1
) else if "%~1" == "--dry-run" (
   set "pipedry=1"
   shift /1
) else (
   goto :pipe-options-end
)

goto :pipe-options-begin
:pipe-options-end

:: ========================================================================

if "%~1" == "" (

   rem command | 2

   set "pipecmd=notepad"
   set "pipetitle=[app = notepad]"
   set "pipeext=.txt"

) else if exist "%~dpn0.%~n1.bat" (

   rem command | 2 app[.ext]

   call :pipe-configure "%~dpn0.%~n1.bat" "%~x1"

   set "pipetitle=[app = %~n1]"
   if not defined pipeext set "pipeext=%~n1"
   if not "%~x1" == "" set "pipeext=%~x1"

   shift /1

) else (

   rem command | 2 ext

   for /f "tokens=1,* delims==" %%a in ( '
      2^>nul assoc ".%~n1"
   ' ) do for /f "tokens=1,* delims==" %%c in ( '
      2^>nul ftype "%%b"
   ' ) do (

      set "pipecmd=%%d"
      set "pipetitle=[%%a = %%b]"
      set "pipeext=%%a"

   )
   if not "%~x1" == "" set "pipeext=%~x1"

   shift /1

)

if defined pipedbg call :pipe-debug "After parsing options"

:: ========================================================================

if not defined pipecmd (
   >&2 echo:Bad invocation
   goto :EOF
)

:: ========================================================================

if not defined pipeext set "pipeext=.txt"
if not defined pipetitle set "pipetitle=[%pipeext%]"

for %%f in ( "%pipetmpdir%" ) do set "pipetmpfile=%%~ff\%pipetmpname%%pipeext%"

:: ========================================================================

setlocal enabledelayedexpansion

set "pipecmdopt="

:pipe-app-options-begin
set pipecmdopt=%1
if not defined pipecmdopt goto :pipe-app-options-end

set "pipecmdopts=%pipecmdopts% %pipecmdopt%"
shift /1

goto :pipe-app-options-begin
:pipe-app-options-end

set "pipecmd=!pipecmd:%%1="%%1"!"
set "pipecmd=!pipecmd:""%%1""="%%1"!"
if "!pipecmd!" == "!pipecmd:%%1=!" set "pipecmd=!pipecmd! "%%1""
set "pipecmd=!pipecmd:%%1=%pipetmpfile%!"

endlocal & set "pipecmd=%pipecmd%" & set "pipecmdopts=%pipecmdopts%"

:: ========================================================================

if defined pipedbg call :pipe-debug "Before invocation"

call :pipe-invoke %pipecmdopts%

endlocal
goto :EOF

:: ========================================================================

:pipe-configure
setlocal
call "%~1" "%~2"
endlocal & set "pipecmd=%pipecmd%" & set "pipeext=%pipeext%" & set "pipetmpsave=%pipetmpsave%"
goto :EOF

:pipe-invoke
if defined pipedry (
   echo:Invocation ^(dry-run^)
   echo:call %pipetmpsave% ^> "%pipetmpfile%"
   echo:call start "Starting %pipetitle%" %pipecmd%
   goto :EOF
)

call %pipetmpsave% > "%pipetmpfile%"
call start "Starting %pipetitle%" %pipecmd%
goto :EOF

:: ========================================================================

:pipe-help
for /f "tokens=1,* delims= " %%a in ( '
   findstr /b "::HELP" "%~f0"
' ) do (
   echo:%%~b
)
goto :EOF

:pipe-debug
echo:%~1...
set pipe
echo:
goto :EOF

:: ========================================================================

:: EOF
Last edited by siberia-man on 29 Oct 2016 16:53, edited 5 times in total.

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Redirect STDOUT to GUI

#2 Post by aGerman » 24 Oct 2016 08:56

Displaying the text in a web browser is a little more tricky.

Code: Select all

assoc /? | 2 html
Output in IE11:

Code: Select all

Zeigt die Zuordnungen mit den Dateierweiterungen an bzw. „ndert sie. ASSOC [.ERW[=[Dateityp]]] .ERW Gibt die Dateierweiterung fr die Zuordnung mit dem Dateityp an. Dateityp Gibt den Dateityp fr die Zuordnung mit der Dateierweiterung an. Geben Sie ASSOC ohne Parameter ein, um die aktuellen Zuordnungen anzuzeigen. Wenn ASSOC mit einer bestimmten Dateierweiterung aufgerufen wird, wird die Zuordnung mit der jeweiligen Dateierweiterung angezeigt. Wenn Sie keinen Datei- typ angegeben haben, wird die Zuordnung fr diese Dateierweiterung gel”scht. 

Line breaks are gone.
Indentations are gone.
Non-ASCII characters are displayed wrong or invisible (such as ÄÖÜäöüß).

In order to avoid those clashes you need to specify the charset and you have to use <pre></pre> tags.
Quick and dirty "html.bat":

Code: Select all

@echo off &setlocal DisableDelayedExpansion
for /f "tokens=2 delims=:" %%i in ('chcp') do set /a encoding=%%~ni
set "fname=pipe.%random%.%random%.html"

>"%temp%\%fname%" (
  <nul set /p "=<!doctype html><html><head><meta charset="cp%encoding%"><title>%fname%</title></head><body><pre>"

  for /f "delims=" %%i in ('find /n /v ""') do (
    set "line=%%i"
    setlocal EnableDelayedExpansion
    echo(!line:*]=!
    endlocal
  )

  <nul set /p "=</pre></body></html>"
)

start "" "%temp%\%fname%"



Code: Select all

assoc /? | html
Output:

Code: Select all

Zeigt die Zuordnungen mit den Dateierweiterungen an bzw. ändert sie.

ASSOC [.ERW[=[Dateityp]]]

  .ERW      Gibt die Dateierweiterung für die Zuordnung mit dem Dateityp an.
  Dateityp  Gibt den Dateityp für die Zuordnung mit der Dateierweiterung an.

Geben Sie ASSOC ohne Parameter ein, um die aktuellen Zuordnungen anzuzeigen.
Wenn ASSOC mit einer bestimmten Dateierweiterung aufgerufen wird, wird die
Zuordnung mit der jeweiligen Dateierweiterung angezeigt. Wenn Sie keinen Datei-
typ angegeben haben, wird die Zuordnung für diese Dateierweiterung gelöscht.

Steffen

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: Redirect STDOUT to GUI

#3 Post by siberia-man » 24 Oct 2016 09:06

Of course, displaying of a plain text in a browser is not good idea because of the reasons you hvae mentioned. But you could do the following thing:

Code: Select all

assoc /? | 2 html.txt


A browser is fine for html output like below (let's assume there is some useful command that outputs real HTML page):

Code: Select all

echo ^^^<h1^^^>hello world^^^</h1^^^> | 2 html

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Redirect STDOUT to GUI

#4 Post by aGerman » 24 Oct 2016 09:16

siberia-man wrote:But you could do the following thing:

Code: Select all

assoc /? | 2 html.txt

That returns "Bad invocation" :?

siberia-man wrote:A browser is fine for html output like below (let's assume there is some useful command that outputs real HTML page):

Code: Select all

echo ^^^<h1^^^>hello world^^^</h1^^^> | 2 html

Yes I think WMIC has an option to output html.

Steffen

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: Redirect STDOUT to GUI

#5 Post by siberia-man » 24 Oct 2016 09:36

That returns "Bad invocation"

I see your point.

I have the file "2.html.bat" configured to launch FF for html output. You don't. So the following invocation does work in my case and doesn't work in yours:

Code: Select all

assoc /? | 2 html.txt


I am going to rework the script (I think it is good thing) to enable processing some output even it is not configured. For example, after reworking the script the following example should work with or without 2.htm.bat, as well:

Code: Select all

assoc /? | 2 htm.txt

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Redirect STDOUT to GUI

#6 Post by aGerman » 24 Oct 2016 09:48

siberia-man wrote:I have the file "2.html.bat" configured

Oh yes I missed that. Now that I used it line breaks and indentations are kept. It however doesn't solve the encoding issue :wink:

Steffen

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: Redirect STDOUT to GUI

#7 Post by siberia-man » 24 Oct 2016 12:04

It however doesn't solve the encoding issue

It shouldn't be solved at all. If you really want to see something as a plain text in browser you need to pass it through another filter to add html-tags and replace some unprintable entities.

update:

Code: Select all

type z-utf.txt
ÄÖÜäöüß

type z.bat
@echo off
<nul set /p "=<!doctype html><html><head><meta charset='utf-8'><title>example</title></head><body><pre>"
timeout /t 0 >nul 2>&1 || more
<nul set /p "=</pre></body></html>"

type z-utf.txt | z | 2 html

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: Redirect STDOUT to GUI

#8 Post by siberia-man » 24 Oct 2016 12:41

Ok. I have updated the script in the first post of this topic. Now it is more flexible now:

Code: Select all

command | 2 html.txt


The code above does work properly in any case -- if the file "2.html.bat" does or doesn't exist.
This works for other associated extensions (not html only).

Thank you, Steffen, for useful discussion.

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Redirect STDOUT to GUI

#9 Post by aGerman » 24 Oct 2016 13:33

Thank you for the useful tool :D

It shouldn't be solved at all.

Oh. In your initial post you were writing that much about displaying the text in a browser so I thought one of your goals was to generate a proper output.

If you really want to see something as a plain text in browser you need to pass it through another filter to add html-tags and replace some unprintable entities.

Of course. That's what I already did in my little code. (Reading the piped text could have been simplified to one line FIND /V "". I didn't use MORE because it has a limitation that I forgot unfortunately). The reason why I didn't use "charset='utf-8'" is that the codepage of the piped text is 850 in my case. I parsed the output of CHCP and saved it in a variable. The result is "charset='cp850'" (with 'cp' prepended to the found number).

Steffen

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: Redirect STDOUT to GUI

#10 Post by siberia-man » 24 Oct 2016 14:22

Oh. In your initial post you were writing that much about displaying the text in a browser so I thought one of your goals was to generate a proper output.


Yes. Our concern is just capture output and invoke the proper GUI application. Once we run some command (or chain of commands) we know the format of the output. So we know which application should be invoked. With the script we do this. If the output is not proper for this application we should pass it through additional filter to fit the application.

Two examples from real life (that inspired to develop early and raw version of the current script):

Code: Select all

config2html some.config | 2 html
preprocess some.cfg | config2html | 2 html

some.config and some.cfg are important files I am keeping in a repository. config2html and preprocess are perl scripts processing the files. So every time I have original files and can see them in my favorite browser without pollution to the working directory or concerns regarding handling with the temp files.

Reading the piped text could have been simplified to one line FIND /V "". I didn't use MORE because it has a limitation that I forgot unfortunately


Me too. I remember that more has some issues but don't remember which ones. But this point is configurable as well. Look at %pipetmpsave% variable:

Example from the help:

Code: Select all

set "pipetmpsave=findstr "$""


Possible usage in your preferences:

Code: Select all

set "pipetmpsave=find /v """


I didn't use "charset='utf-8'" is that the codepage of the piped text is 850 in my case

Charset=utf-8 is used just for example. It is assumed as universal code page, installed on all modern computers and doesn't have possible problems with printing them on the screen.

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Redirect STDOUT to GUI

#11 Post by aGerman » 24 Oct 2016 14:54

Everything makes perfect sense now :)

Found something about the limitation of MORE:
viewtopic.php?t=5937&p=37069#p37069

Steffen

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: Redirect STDOUT to GUI

#12 Post by siberia-man » 25 Oct 2016 23:01

I have updated the script. It is now possible to configure the script globally with auxiliary file "2-settings.bat". Using it we can set some specific variables (%pipetmpdir%, %pipetmpname% and %pipetmpsave%).

Post Reply