0

The situation

I'm sitting at the receiving end of program A (redmon, a port monitor). I need to write a batch script that program A can call to pipe a large amount of data (PostScript) into.

Whenevern program A calls my batch script, I need to pass that data on to program B (Ghostscript) and eventually have program C (called dosomething here) do something with the output of program B.

How it would work with Bash (Unix shell)

In a bash script, I'd write a script (let's call it myscript.sh) like:

#!/bin/bash
cat - | gs -sDEVICE=pdfwrite -o test.pdf -f -
dosomething test.pdf

This would work work perfectly when one pipes some data into it:

cat testdata.ps | myscript.sh

Will it work on with a Windows Batch file?

As this is for Windows, I need something that can be called like

type testdata.ps | myscript.bat

because, effectively, this is how program A would call myscript.bat. So I tried translating the bash approach 1:1 to a batch file:

@echo off
type con | "%PROGRAMFILES%\gs\gs9.16\bin\gswin32c" -o test.pdf -f -
dosomething test.pdf

The problem is, type con doesn't seem to recognize when there's nothing left to process and the batch script therefore hangs. So, what can I do?

I tried if it might work in reverse, like

"%PROGRAMFILES%\gs\gs9.16\bin\gswin32c" -o test.pdf -f - < CON

but no luck either. An attempt at copying CON to a file failed as well

copy con test.ps

How would one go about this? It all boils down to capturing the entire piped content and recognizing when program A is closing the pipe.

Thomas W
  • 14,757
  • 6
  • 48
  • 67
  • When exactly _there's nothing left to process_? Or how can `cat` figure that out since it's user input? – CristiFati Aug 05 '15 at 17:57
  • Is that bash script program A or program B? – SomethingDark Aug 05 '15 at 18:01
  • @CristiFati Good questions - program A pipes out an entire file. `bash` would notice when the `stdin` pipe is "closed". I'm wondering how a batch script could notice as well. – Thomas W Aug 05 '15 at 20:14
  • You can't use `con` with piped input. The `con` device refers to the interactive console, not to the standard input. But if the input to the batch script is redirected then the child will inherit that redirected input, so you shouldn't need to do anything - just have the batch script run the program. – Harry Johnston Aug 05 '15 at 21:26
  • @HarryJohnston I just noticed this a few seconds before you wrote this. Much simpler than I thought! Many thanks. Would you add this as an answer so I can mark the question as answered? – Thomas W Aug 05 '15 at 21:28

3 Answers3

3

You're trying too hard; Windows will do what you want by default, so you just need to say

@echo off
"%PROGRAMFILES%\gs\gs9.16\bin\gswin32c" -o test.pdf -f -
dosomething test.pdf

The reason type con or < CON don't work is that CON is the local console device and is not affected by redirection.

(Incidentally, I think this is also true in Linux; using cat - | myprog is harmless but redundant.)

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • You're totally right - works without re-piping a second time, both in Windows and Linux. The only remaining thing that would be great to learn is how the receiving script could redirect the piped data to a file. – Thomas W Aug 05 '15 at 21:37
  • 1
    Try `more > myinput.txt`, I think that should work, at least provided the input is text and not binary, and provided the parent process closes the pipe when it reaches the end of the data. (When output is to a console, the `more` command paginates it, but not when output is to a file or pipe.) – Harry Johnston Aug 05 '15 at 22:37
  • Oh, great - works! I was already looking into [this alleged `tee` port](http://www.robvanderwoude.com/files/tee_nt.txt) which would however omit `]` at the beginning of lines. – Thomas W Aug 05 '15 at 22:43
  • @ThomasW - [That Rob van der Woude tee.bat script is worthless](http://stackoverflow.com/a/10719322/1012053). An equivalent version, without the `]` limitation, could be written as simply as `@if /i "%~1"=="-a" (findstr "^" >>"%~2" && type "%~2") else findstr "^" >"%~1" && type "%~1"`. But that one liner is still useless in my book. That link I gave you has a very useful script based tee command that is properly asynchronous, if you ever need one for Windows batch. – dbenham Aug 06 '15 at 12:27
  • @dbenham Thanks for that. Asynchronicity isn't essential in this case, but it's good to learn more about the background. – Thomas W Aug 06 '15 at 13:49
2

In batch scripting, if you want to capture input from a pipe, use set /P. Here's an example:

@echo off
setlocal

set /p "piped="

echo piped: %piped%

Example session:

command: echo Hello world! | batfile.bat
output: piped: Hello world!


For extra credit, you can code your script so that it'll accept input either via argument or pipe.

@echo off
setlocal

if "%~1"=="" (
    set /p "input="
) else set "input=%*"

echo input: %input%

In that example, if there is no Argument 1, then capture from stdin. Otherwise, set input=all arguments.

command: echo Hello world! | batfile.bat
output: input: Hello world!

command: batfile.bat Hello World!
output: input: Hello world!

command: >textfile.txt echo Hello world!
command 2: batfile.bat < textfile.txt
output: input: Hello world!


Be advised that set /P "var=Question?" stops capturing as soon as it receives a new line. So it's not well-suited for binary streams I don't think. You will probably have to save the PostScript stream to a .ps file, then gswin32c -o test.pdf temp.ps or similar.

I'm not really clear on what you mean by being at the receiving end of Program A. Do you have control over Program A's command line? If so, why not Program A | gswin32c -o test.pdf -?

Community
  • 1
  • 1
rojo
  • 24,000
  • 5
  • 55
  • 101
  • Thanks for your very detailed answer. Yes, the problem is that the PostScript data is thosands of lines long and potentiall containing binary data. Program A is a port monitor and will pipe the data it receives to whatever it's been told to pipe into. – Thomas W Aug 05 '15 at 20:48
  • 1
    SET /P can only capture a single line, with lots of limitations: max length 1021 bytes, trailing control chars stripped, and I believe it terminates at first null byte. – dbenham Aug 06 '15 at 12:32
1

To tell con to stop you need to pass over an end-of-file character (code 26 or 0x1A), which can be achieved by pressing <Ctrl+Z> in command prompt.

So copy con test.ps will work when you terminate your input that way. (You still have to press <enter> finally; everything after the end-of-file will be discarded.)

The end-of-file code is displayed as ^Z in command prompt.

aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • This is not an interactive situation, so there's no way to press any keys here. However, there is an option that the PostScript driver sends a `Ctrl+D` at the end of the data - so thanks for the hint. Maybe that can be useful for find a solution, though I can't rule out there might be a `Ctrl+D` character in the data itself. – Thomas W Aug 05 '15 at 20:58