如何在批处理脚本中将控制台输出保存到文件并打印输出?

154
我有一个批处理脚本,它执行任务并将输出发送到文本文件。是否有一种方法让输出也显示在控制台窗口中?
例如:
c:\Windows>dir > windows-dir.txt

有没有办法让dir的输出既显示在控制台窗口中,又将其放入文本文件中?


这里解释了你可以进行的重定向以及一些示例。<br>希望能对你有所帮助 :) - emoSTN
请注意,@Vadzim,2009年2月2日16:38提出的问题不能被视为2009年4月28日06:32几乎三个月后提出的问题的重复。 - Compo
12个回答

236

不能通过纯重定向实现,但是可以使用一些技巧(如tee.bat)来实现。

我尝试解释一下重定向。

使用> 文件名< 文件名将其中一个十个流重定向到文件中。
无论重定向是在命令之前还是之后都不重要,因此这两行代码几乎相同。

dir > file.txt
> file.txt dir

这个例子中的重定向仅仅是1>的一种简写,这意味着流1(STDOUT)将被重定向。
所以你可以通过在前面加上数字来重定向任何流,比如2> err.txt,也允许在同一行中重定向多个流。

dir 1> files.txt 2> err.txt 3> nothing.txt
在这个例子中,“标准输出”将进入files.txt,所有错误将在err.txt中,而stream3将进入nothing.txt(DIR不使用流3)。 Stream0是STDIN, Stream1是STDOUT, Stream2是STDERR, Stream3-9未使用。 但是,如果您尝试多次重定向相同的流会发生什么呢?
dir > files.txt > two.txt

"只能有一个",它总是最后一个!
所以它等同于 dir > two.txt

好的,还有一种额外的可能性,将一个流重定向到另一个流。

dir 1>files.txt 2>&1 

2>&1将stream2重定向到stream1,1>files.txt将所有内容重定向到files.txt
这里的顺序很重要!

dir ... 1>nul 2>&1
dir ... 2>&1 1>nul

两者不同。第一行将所有输出(STDOUT和STDERR)重定向到NUL,
但第二行将STDOUT重定向到NUL,将STDERR重定向到“空”STDOUT。

一个结论是,很明显为什么Otávio Décio和andynormancx的示例无法工作。

command > file >&1
dir > file.txt >&2

两者都试图重定向stream1两次,但是“只能有一个”,并且它始终是最后一个。
所以你得到

command 1>&1
dir 1>&2

在第一个示例中,将流stream1重定向到自身是不允许的(也没有多大用处)。


28
无法使用本机的Windows Shell实现。 - Fantastory
1
第二个机制“处理重复”怎么样?我尝试了3<&2(注意是LT而不是GT),然后是3>errors.txt。但这样做的结果很糟糕 - 所有随后的stderr输出都被捕获到了errors.txt中(也就是说,来自其他命令的输出也被捕获了!)。 - Tomasz Gandor
你可以使用 for /f "delims=" %%v in ('dir') do echo %%v>>file.txt - amegyoushi
3
@CharlesMcKelvey(以及所有投赞成票的人),如果有一个回答只回答“不”,你会接受吗?或者你更希望得到一些解释呢? - Rodrigo.A92

37

只需使用Windows版本的UNIX tee命令(从http://unxutils.sourceforge.net获取),方法如下:

mycommand > tee outpu_file.txt
如果你还需要 STDERR 输出,那么请使用以下命令。
2>&1 会将 STDERR 输出与 STDOUT(主要流)合并。
mycommand 2>&1 | tee output_file.txt

4
PowerShell Tee-Object 提供类似的功能。Get-Process | Tee-Object -file c:\scripts\test.txt - Kevin Obee
1
我在PowerShell和普通shell中都试过了。1>tee a.txt会将stdout重定向到一个名为tee而不是调用tee命令的文件。有人可以确认这个答案对他们有效吗? - mafu
@mafu 我可以确认这一点。 - Cerno
@mafu 上面的答案有点不正确。在顶部的例子中,您必须使用管道“|”而不是“>”。奇怪的是,在这种情况下,命令行中的STDOUT和STDERR输出顺序被混淆了。但是对我来说,底部的例子完美地运行。 - Cerno
此外,SourceForge 下载对我来说有问题。我在这里找到了一个替代主机:https://www.wzw.tum.de/public-html/syring/win32/UnxUtilsDist.html(慕尼黑大学)。 - Cerno
格式对我不起作用。我不得不使用'mycommand | tee outpu_file.txt'。 - SpecialK

12
如果您不需要实时输出结果(即在程序编写时),可以添加:
type windows-dir.txt

在那行之后。


如果要将输出重定向到文本文件,可以在命令末尾使用以下语法:dir > windows-dir.txt & type windows-dir.txt。对于代码块也是如此:( echo %date% <NL> echo %username% <NL> echo %time% <NL> pause>CON )>test.txt & type test.txt。然而,如果需要实时输出,则可以使用例如 tee 工具的 Windows 版本来实现… - mousio

8

2
这适用于低输出命令。对于大量输出的命令,它不起作用,因为它只显示第一个缓冲区。他需要实时输出并将输出保存到日志文件中。 - NetVicious
如果你使用 >> 而不是 >,那会有问题。 - Nime Cloud

7

是的,有一种方法可以在控制台(屏幕)和文件中显示单个命令的输出。使用您的示例,使用...

@ECHO OFF
FOR /F "tokens=*" %%I IN ('DIR') DO ECHO %%I & ECHO %%I>>windows-dir.txt

详细解释:

FOR 命令将命令或文本的输出解析为可多次引用的变量。

对于命令,例如 DIR /B,请如下示例中所示用单引号括起来。将 DIR /B 文本替换为您想要的命令。

FOR /F "tokens=*" %%I IN ('DIR /B') DO ECHO %%I & ECHO %%I>>FILE.TXT

要显示文本,将文本用双引号括起来,如下例所示。

FOR /F "tokens=*" %%I IN ("Find this text on console (screen) and in file") DO ECHO %%I & ECHO %%I>>FILE.TXT

...并且有换行...

(这是需要翻译的内容)
FOR /F "tokens=*" %%I IN ("Find this text on console (screen) and in file") DO (
  ECHO %%I & ECHO %%I>>FILE.TXT
)

如果您有时只想将输出显示在控制台(屏幕)上,有时只想将其发送到文件中,有时则同时发送到两者,请使用变量指定FOR循环的“DO”子句,如下所示,其中%TOECHOWHERE%

@ECHO OFF
FOR %%I IN (TRUE FALSE) DO (
  FOR %%J IN (TRUE FALSE) DO (
    SET TOSCREEN=%%I & SET TOFILE=%%J & CALL :Runit)
)
GOTO :Finish

:Runit
  REM Both TOSCREEN and TOFILE get assigned a trailing space in the FOR loops
  REM above when the FOR loops are evaluating the first item in the list,
  REM "TRUE".  So, the first value of TOSCREEN is "TRUE " (with a trailing
  REM space), the second value is "FALSE" (no trailing or leading space).
  REM Adding the ": =" text after "TOSCREEN" tells the command processor to
  REM remove all spaces from the value in the "TOSCREEN" variable.
  IF "%TOSCREEN: =%"=="TRUE" (
      IF "%TOFILE: =%"=="TRUE" (
          SET TEXT=On screen, and in "FILE.TXT"
          SET TOECHOWHERE="ECHO %%I & ECHO %%I>>FILE.TXT"
        ) ELSE (
          SET TEXT=On screen, not in "FILE.TXT"
          SET TOECHOWHERE="ECHO %%I"
      )
    ) ELSE (
      IF "%TOFILE: =%"=="TRUE" (
          SET TEXT=Not on screen, but in "FILE.TXT"
          SET TOECHOWHERE="ECHO %%I>>FILE.txt"
        ) ELSE (
          SET TEXT=Not on screen, nor in "FILE.TXT"
          SET TOECHOWHERE="ECHO %%I>NUL"
      )
  )
  FOR /F "tokens=*" %%I IN ("%TEXT%") DO %TOECHOWHERE:~1,-1%
GOTO :eof

:Finish
  ECHO Finished [this text to console (screen) only].
  PAUSE

你应该使用 delims= - amegyoushi

3

我的建议是:

创建一个子程序,接收消息并自动将其发送到控制台和日志文件。

setlocal
set logfile=logfile.log

call :screenandlog "%DATE% %TIME% This message goes to the screen and to the log"    

goto :eof

:screenandlog
set message=%~1
echo %message% & echo %message% >> %logfile%
exit /b

如果您在消息中添加一个变量,请确保在将其发送到子例程之前删除其中的引号,否则它可能会破坏您的批处理。当然,这仅适用于回显。


哇!太棒了!我真的很喜欢这个!不过我做了三个更改,希望你喜欢。1) 我使用 setlocal enabledelayedexpansion 而不是只用 setlocal 2) 在子程序中,我使用延迟扩展来处理日志文件变量,所以用 >> !logfile! 代替 >> %logfile%。这样,在代码块内(比如在 if 语句内)调用时就能正常工作。3) 我删除了 "set message" 行,因为没有必要再创建一个额外的变量。我们已经有 $~1 中的内容了,所以我将两个 echo 命令都改成了 echo $~1 - Nate

2

如果您想追加而不是替换输出文件,则可以使用

dir 1>> files.txt 2>> err.txt

或者

dir 1>> files.txt 2>>&1

2
我创建了一个简单的C#控制台程序,可以实时输出到cmd屏幕和日志。
class Tee
{
    static int Main(string[] args)
    {
        try
        {
            string logFilePath = Path.GetFullPath(args[0]);

            using (StreamWriter writer = new StreamWriter(logFilePath, true))
            {
                for (int value; (value = Console.In.Read()) != -1;)
                {
                    var word = Char.ConvertFromUtf32(value);
                    Console.Write(word);
                    writer.Write(word);
                }
            }
        }
        catch (Exception)
        {
            return 1;
        }
        return 0;
    }
}

批处理文件的使用方式与您使用Unix中的tee命令相同。
foo | tee xxx.log

如果你没有编译工具,这里有一个包含Tee.exe的存储库 https://github.com/iamshiao/Tee


2
command > file >&1

当您不将数据写入屏幕时,它会更快,因为您不会用垃圾填充缓冲区,而是让批处理作业运行。如果您正在调试,则不建议使用“>文件”语法。 - Random Developer
@andynormancx 嗯,我忘记打那个了,应该是手动输入而不是从CMD中复制粘贴。 - Random Developer
我无法在Windows命令提示符下使语法正常工作,因此改用了type命令。 - JamesEggers
&2确实可以工作,但是当我测试它时,文件现在没有被创建。 - JamesEggers
6
如果我执行命令 "dir > file.txt >&2",则不会创建 file.txt 文件。虽然在控制台上会输出目录的内容,但文件并没有被创建。只有当去掉 ">&2" 参数时才会创建该文件。 - JamesEggers
显示剩余2条评论

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接