为什么有些命令会处理已被其他命令消耗的重定向STDIN数据行?

时间:2016-05-24 20:39:11

标签: batch-file cmd io-redirection

假设我们将以下代码段与文本文件sample.txt重定向到STDIN

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

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

first
second
third
fourth

控制台上返回的输出将是这个,这正是我所期望的(first消耗了行secondset /P,因此findstr收到了并处理其余的行):

third
fourth
first, second

findstr /R "^"替换为sort /R时,会产生相同的输出。

但是,在findstrfind /V ""替换more命令行时,输出将为:

first
second
third
fourth
first, second

虽然set /P已经消耗了最后一行输出行证明的行firstsecond,但findmore仍然会收到整个重定向数据。

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

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

2 个答案:

答案 0 :(得分:1)

  

为什么有些命令会处理已被其他命令消耗的重定向STDIN数据行?

因为一些命令/程序倒带stdin。你可以试试这个:

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

结果:

    third
    fourth
    first, second

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

答案 1 :(得分:1)

好吧,关于命令为何以其行为方式运行的问题的即时解答位于Aacinicomment中:»“因为这样的命令是以这种方式编写的”。

无论如何,一段时间之后,我想收集我的发现,并最终提出我最近发现的新解决方法。

只有少数命令似乎不重置数据指针,并且每个命令都有其优缺点:

  1. 在问题中已经证明了使用findstr返回剩余数据的用法。当重定向的输入数据没有通过最后的换行符终止时,findstr可能会挂起问题:What are the undocumented features and limitations of the Windows FINDSTR command?

  2. pause不会重置数据指针(这实际上就是我想要在此处提到它的原因),而与数据是来自输入重定向还是来自管道无关,< strong>但是,不幸的是,它没有提供任何消耗的字符。

  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。另外请注意,换行符也算在内。