【问题标题】:passing on piped data in batch file在批处理文件中传递管道数据
【发布时间】:2015-08-05 17:41:35
【问题描述】:

情况

我坐在程序 A(redmon,端口监视器)的接收端。我需要编写一个批处理脚本,程序 A 可以调用它来将大量数据(PostScript)导入。

每当程序 A 调用我的批处理脚本时,我都需要将该数据传递给程序 B(Ghostscript),并最终让程序 C(此处称为 dosomething)对程序 B 的输出进行处理。

它如何与 Bash(Unix shell)一起工作

在 bash 脚本中,我会编写一个脚本(我们称之为 myscript.sh),例如:

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

当有人将一些数据输入其中时,这将完美地工作:

cat testdata.ps | myscript.sh

它是否适用于 Windows 批处理文件?

因为这是针对 Windows 的,所以我需要一些可以像这样调用的东西

type testdata.ps | myscript.bat

因为,实际上,这就是程序 A 调用myscript.bat 的方式。所以我尝试将 bash 方法 1:1 转换为批处理文件:

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

问题是,type con 似乎无法识别什么时候没有任何东西需要处理,因此批处理脚本会挂起。那么,我该怎么办?

我试过它是否可以反过来工作,比如

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

但也没有运气。将CON 复制到文件的尝试也失败了

copy con test.ps

如何解决这个问题?这一切都归结为捕获整个管道内容并识别程序 A 何时关闭管道。

【问题讨论】:

  • 什么时候没有任何东西需要处理?或者cat 既然是用户输入,怎么能算出来?
  • 那个bash脚本是程序A还是程序B?
  • @CristiFati 好问题 - 程序 A 通过管道输出整个文件。 bash 会注意到 stdin 管道何时“关闭”。我想知道批处理脚本如何也能注意到。
  • 您不能将con 与管道输入一起使用。 con 设备指的是交互式控制台,而不是标准输入。但是如果批处理脚本的输入被重定向,那么子进程将继承重定向的输入,因此您不需要做任何事情 - 只需让批处理脚本运行程序即可。
  • @HarryJohnston 在你写这篇文章前几秒钟我才注意到这一点。比我想象的要简单得多!非常感谢。您是否可以将此添加为答案,以便我可以将问题标记为已回答?

标签: windows batch-file pipe


【解决方案1】:

你太努力了;默认情况下Windows会做你想做的,所以你只需要说

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

type con&lt; CON 不起作用的原因是CON 是本地控制台设备,不受重定向的影响。

(顺便说一句,我认为这在 Linux 中也是如此;使用 cat - | myprog 是无害的,但也是多余的。)

【讨论】:

  • 你说得对——在 Windows 和 Linux 中都无需再次重新管道即可工作。剩下的唯一值得学习的是接收脚本如何将管道数据重定向到文件。
  • 试试more &gt; myinput.txt,我认为这应该可行,至少如果输入是文本而不是二进制,并且父进程在到达数据末尾时关闭管道。 (当输出到控制台时,more 命令对其进行分页,但当输出到文件或管道时不会。)
  • 哦,太棒了 - 工作!我已经在研究this alleged tee port,但是会在行首省略]
  • @ThomasW - That Rob van der Woude tee.bat script is worthless。没有] 限制的等效版本可以写成@if /i "%~1"=="-a" (findstr "^" &gt;&gt;"%~2" &amp;&amp; type "%~2") else findstr "^" &gt;"%~1" &amp;&amp; type "%~1"。但那一个班轮在我的书中仍然没用。我给你的那个链接有一个非常有用的基于脚本的 tee 命令,如果你需要一个用于 Windows 批处理的命令。
  • @dbenham 谢谢你。在这种情况下,异步性不是必需的,但最好了解有关背景的更多信息。
【解决方案2】:

在批处理脚本中,如果您想从管道捕获输入,请使用set /P。这是一个例子:

@echo off
setlocal

set /p "piped="

echo piped: %piped%

示例会话:

命令: echo Hello world! | batfile.bat
输出:管道:Hello world!


为了获得额外奖励,您可以编写脚本,使其接受通过参数或管道输入的输入。

@echo off
setlocal

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

echo input: %input%

在该示例中,如果没有参数 1,则从标准输入捕获。否则,设置input=all arguments

命令: echo Hello world! | batfile.bat
输出: 输入:Hello world!

命令: batfile.bat Hello World!
输出: 输入:Hello world!

命令: &gt;textfile.txt echo Hello world!
命令 2: batfile.bat &lt; textfile.txt
输出: 输入:Hello world! p>


请注意,set /P "var=Question?" 在收到新行后会立即停止捕获。所以我认为它不太适合二进制流。您可能必须将 PostScript 流保存为 .ps 文件,然后是 gswin32c -o test.pdf temp.ps 或类似文件。

我不太清楚你在程序 A 的接收端是什么意思。你可以控制程序 A 的命令行吗?如果是这样,为什么不Program A | gswin32c -o test.pdf -

【讨论】:

  • 感谢您非常详细的回答。是的,问题在于 PostScript 数据有数千行长并且可能包含二进制数据。程序 A 是一个端口监视器,它将接收到的数据通过管道传输到它被告知要通过管道传输的任何内容。
  • SET /P 只能捕获一行,有很多限制:最大长度 1021 字节,尾随控制字符被剥离,我相信它会在第一个空字节处终止。
【解决方案3】:

要告诉con 停止,您需要传递一个文件结尾字符(代码26 或0x1A),这可以通过在命令提示符下按&lt;Ctrl+Z&gt; 来实现。

因此,当您以这种方式终止输入时,copy con test.ps 将起作用。 (最后还是要按&lt;enter&gt;,文件结束后的所有内容都将被丢弃。)

文件结束代码在命令提示符中显示为^Z

【讨论】:

  • 这不是交互式情况,所以这里没有办法按任何键。但是,PostScript 驱动程序可以选择在数据末尾发送Ctrl+D - 所以感谢您的提示。也许这对于找到解决方案很有用,但我不能排除数据本身中可能存在Ctrl+D 字符。
猜你喜欢
  • 2015-02-26
  • 1970-01-01
  • 2021-06-04
  • 1970-01-01
  • 2019-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多