【问题标题】:Why do some commands process lines of redirected STDIN data which are already consumed by other commands?为什么某些命令会处理已被其他命令使用的重定向 STDIN 数据行?
【发布时间】:2016-05-24 20:39:10
【问题描述】:

假设我们有以下代码 sn-p 将文本文件 sample.txt 重定向到 STDIN

@echo off
< "sample.txt" (
    set /P "ONE="
    set /P "TWO="
    findstr /R "^"
)
echo %ONE%, %TWO%

...以及相关文本文件sample.txt的内容:

first
second
third
fourth

控制台上返回的输出将是这个,这正是我所期望的(firstsecond 行被set /P 消耗,因此findstr 接收并处理剩余的行):

third
fourth
first, second

findstr /R "^" 替换为sort /R 时,得到相同的输出。

但是,当用find /V ""more 替换findstr 命令行时,输出将是:

first
second
third
fourth
first, second

似乎set /P 已经消耗了firstsecond 行,这由最后的输出行证明,findmore 仍然接收到整个重定向数据。

为什么会这样,是什么导致了这种行为?有没有办法强制findmore 只接收前面命令尚未处理的剩余重定向数据?

(将输出数据STDOUT重定向到文本文件时的行为是相同的。同样当直接在cmd中执行类似于上述批处理代码的命令行时,没有任何变化。)

【问题讨论】:

  • 您可能会阅读此行为的描述here,但您的问题的答案是:“因为这样的命令是这样编程的”
  • 非常有趣,@Aacini!我已经怀疑findmore 重置了文件指针,因为我玩弄了我提到的所有命令,混合并重新排序它们,我什至在它们周围包裹了一个for /F(比如:for /F "delims=" %%L in ('more') do echo(%%L),这一切都没有改变。所以我担心,似乎没有(本地)方法可以解决这种行为?
  • 您可以尝试将findstr /R "^" 的结果通过管道传输到find /V ""more
  • 好主意,@DennisvanGils,管道允许将findmore 应用于剩余而不是所有数据...
  • 不幸的是,我刚刚发现findstr 方法挂起,以防sample.txt 的最后一行没有被换行符终止;其他命令(sortfindmore)工作正常;所以似乎没有可靠的方法来返回 remaining 行......或者你有什么想法吗?我试图将一个换行符(echo()通过管道传递到括号中的表达式中,但是最后一行丢失了(findstr)...

标签: batch-file cmd io-redirection


【解决方案1】:

为什么某些命令会处理已被其他命令使用的重定向 STDIN 数据行?

因为某些命令/程序会倒带标准输入。你可以试试这个:

@echo off
< "sample.txt" (
    set /P "ONE="
    set /P "TWO="
    more +2
)
echo %ONE%, %TWO%

结果:

    third
    fourth
    first, second

more +2 跳过文件的前两行。

【讨论】:

  • echo %ONE%, %,实际上是指echo %ONE%, %TWO%,对吧?对于手头的代码,这是一个很好的解决方法,但是more 仍然重新处理所有数据(它接收所有行,它只是从输出中跳过两行)...
  • @aschipfl,是的,应该是 %TWO% 而不是 %,我错过了。刚刚编辑了答案。是的,more 仍然倒带标准输入,以便处理整个文件。无论如何,我认为这是您问题的答案,有些程序的行为不像管道链中的适当过滤器。
【解决方案2】:

嗯,关于为什么命令会以这种方式运行的问题的正确答案在于Aacinicomment:»因为这些命令是这样编程的«。

无论如何,经过一段时间后,我想收集我的发现并最终展示我最近发现的新解决方法。

似乎只有少数几个命令不会重置数据指针,并且各有优缺点:

  1. 问题中已经演示了findstr 用于返回剩余数据的用法。当重定向的输入数据未被最终换行符终止时,findstr 可能会挂起的问题:What are the undocumented features and limitations of the Windows FINDSTR command?

  2. pause 不会重置数据指针(这实际上是我想在这里提到它的原因),与数据是来自输入重定向还是来自管道无关,但是 不幸的是,它不会以任何方式提供消耗的角色。

  3. set /P 适用于读取不超过 1 KB 的单行,因此要返回重定向数据的剩余部分,您将需要某种循环:

     @echo off
     rem // Count total number of available lines in advance:
     for /F %%C in ('^< "sample.txt" find /C /V ""') do set "COUNT=%%C"
     < "sample.txt" (
          set /P "ONE="
          set /P "TWO="
          rem /* Loop here to return the rest; `3` is `1 + 2`, where `2`
          rem    is the hard-coded number of lines already handled; you can
          rem    just use `1` here, which will cause read attempty beyond
          rem    the end of data, causing empty lines to be returned: */
          for /L %%N in (3,1,%COUNT%) do (
              rem // Replace `&&` by `&` to NOT skip empty lines:
              set "LINE=" & set /P "LINE=" && call echo(%%LINE%%
          )
     )
     echo %ONE%, %TWO%
    

    注意set /P不能在管道中使用:Piping into SET /P fails due to uninitialised data pointer?

  4. 最后,sort 可用于返回余数。为防止文本行混乱,请使用字符位置选项/+n 并将n 设置为超出实际行长度的数字:

     @echo off
     set "ONE="
     set "TWO="
     < "sample.txt" (
         set /P "ONE="
         set /P "TWO="
         rem /* `sort` with the sort position set beyond the lines of text seems to
         rem    simply revert the current sort order; another reversion restores the
         rem    original sort order; I set the sort position just beyond the maximum
         rem    record or line length, which I set to the greatest possible value: */
         sort /+65536 /REC 65535 | sort /+65536 /REC 65535
     )
     echo %ONE%, %TWO%
    

    我将记录或行长 (/REC) 设置为最大可能值,因为它默认为 4096。请注意,如果您指定的值更少,最小值实际上是 128。另请注意,换行符也被视为计数。

【讨论】:

    猜你喜欢
    • 2012-03-08
    • 2014-11-03
    • 2013-02-09
    • 1970-01-01
    • 2021-06-06
    • 1970-01-01
    • 2010-11-14
    • 2021-09-09
    • 2020-01-20
    相关资源
    最近更新 更多