抑制命令行输出

139

我有一个简单的批处理文件,内容如下:

echo off
taskkill /im "test.exe" /f > nul pause

如果“test.exe”没有运行,则会显示以下错误信息:

ERROR: The process "test.exe" not found.

为什么即使我将输出重定向到nul,也会显示这个错误消息?

如何抑制此输出?

4个回答

239
因为错误信息通常输出到stderr而不是stdout。将调用更改为以下内容:
taskkill /im "test.exe" /f >nul 2>&1

这是可行的,因为按惯例,stdout是文件描述符1,stderr是文件描述符2。(0是stdin)。 2>&1 将输出文件描述符2从刚刚重定向到空设备的1的新值中复制。

这种语法(宽松地)借鉴了许多Unix shell,但您必须小心,因为Shell语法与CMD.EXE之间存在细微的差异。

更新: 我知道OP理解我在此写入的名为NUL的“文件”的特殊性质,但有评论者不明白,所以让我详细说明一下这个方面。

回到最早期的MSDOS版本,某些文件名被文件系统内核抢先使用并用于引用设备。最早的这些名称列表包括NULPRNCONAUXCOM1COM4NUL是空设备,它可以始终打开进行读取或写入,可以在其上写入任意数量,读取始终成功但返回无数据。其他设备包括并口打印机端口、控制台和最多四个串行端口。自MSDOS 5以来,有几个保留名称,但基本约定已非常确立。

当Windows创建时,它作为在MSDOS内核之上的一个相对较薄的应用程序切换层开始运行,因此具有相同的文件名限制。当Windows NT被创建为真正独立的操作系统时,像NULCOM1这样的名称被认为太广泛且适合工作,以允许它们被消除。但是,新设备总会得到将阻止将来使用这些名称的实际文件的名称的想法显然是不合理的。

Windows NT及其后续版本(2K、XP、7和现在的8)都使用更加详细复杂的NT名称空间,从内核代码到精心构造的高度不可移植的用户空间代码都遵循此规则。在该命名空间中,设备驱动程序可以通过\Device文件夹进行访问。为了支持所需的向后兼容性,有一个特殊机制使用\DosDevices文件夹,在任何文件系统文件夹中实现了保留文件名列表。用户代码可以使用Win32 API下面的API层浏览此内部名称空间;一个探索内核命名空间的好工具是微软SysInternals组的WinObj

要了解有关Windows中文件(和设备)合法名称的规则的完整描述,请参阅MSDN此页面,这既有启发性又令人望而生畏。这些规则比本应如此的更加复杂,实际上无法回答一些简单的问题,例如“最长合法完全限定路径名是多少字符长?”。


5
谢谢你的回答,更重要的是谢谢你的解释。 - JosephStyons
一条建议:taskkill /im "test.exe" /f >%temp%\nul 2>&1 & del %temp%\nul。这将防止一个空的 null 文件被放置到本地目录中。 - Samie Bencherif
11
@SamyBencherif,“NUL”是一个保留的文件名,对应于NUL设备。您无法在任何目录中创建名为“NUL”的实际文件。 - RBerteig
1
在PowerShell中失败,出现错误FileStream was asked to open a device that was not a file. For support for devices like 'com1:' or | 'lpt1:', call CreateFile, then use the FileStream constructors that take an OS handle as an IntPtr. - jjxtra
@jjxtra 发现在 PowerShell 中必须这样使用:>$null 2>&1 - Ravior
不仅适用于“taskkill”,而且还适用于所有命令,如rmdir(不存在的错误消息),xcopy,timeout等。 - Gaurav

9
请使用以下脚本代替原有的脚本:
@taskkill/f /im test.exe >nul 2>&1
@pause

2>&1的作用是将stderr输出重定向到stdout。下面我会更好地解释它:

@taskkill/f /im test.exe >nul 2>&1

结束任务 "test.exe"。将stderr重定向到stdout。然后将stdout重定向到nul

@pause

显示暂停消息按任意键继续. . . ,直到有人按下一个键。

注意:符号@隐藏了每个命令的提示。这样可以节省多达8个字节。

你的脚本的最短版本可能是:
@taskkill/f /im test.exe >nul 2>&1&pause
第一次使用&字符进行重定向,第二次用于分隔命令。
在一行中不需要两次使用@字符。尽管你发布的代码长度为49字节,但这段代码只有40字节!我实际上节省了9个字节。请看上面更干净的代码。


是的,我知道一半的帖子实际上是如何缩短您的代码。 - EKons

6

使用>nul 2>&1无法正常运行mysqldump命令。
可以使用2> nul来代替,这样就可以屏蔽掉stderr信息:"Warning: Using a password on the command line interface can be insecure"。


0

你也可以这样做:

tasklist | find /I "test.exe" > nul && taskkill /f /im test.exe > nul

1
尽管问题的上下文是批处理脚本,但我发现在PowerShell中,上述语法会失败并显示“_out-file:FileStream 被要求打开一个不是文件的设备。对于像 'com1:' 或 'lpt1:' 这样的设备,请调用 CreateFile,然后使用接受 OS 句柄作为 IntPtr 的 FileStream 构造函数。”解决方案是将 > nul 替换为 >$null - weir

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