如何确定 powershell 在终端输出的信息是 stdout 还是 stderr?

149 天前
 xinghen57

最近 powershell 中用 ffmpeg ,发现它终端的输出竟然走的 stderr 。

请问 v 友,有方法确认终端输出是 stdout 还是 stderr 吗?或确定终端输出的 stream 号?

1593 次点击
所在节点    PowerShell
17 条回复
matepi
149 天前
你是否需要
2>&1
把 stderr 输出重定向到 stdout ?

如果是程序环境内调用,是否考虑加一层 shell 环境来调用? cmd /C 或者 sh -c
xinghen57
149 天前
@matepi #1 谢谢,想问的不是重定向。
我的意思是,终端上有一行输出,如果没有报错信息,通常我会当成 stdout 。但 ffmpeg ,额,这个有报错信息,ffprobe ,这个没报错信息,它的输出却走的 stderr ,也即 stream 2 。
比如的代码

```
# powershell 下
ffprobe -i 'video.mp4'

# 输出如下
ffprobe version 6.0-full_build-www.gyan.dev Copyright (c) 2007-2023 the FFmpeg developers
built with gcc 12.2.0 (Rev10, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libaribb24 --enable-libdav1d --enable-libdavs2 --enable-libuavs3d --enable-libzvbi --enable-librav1e --enable-libsvtav1 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libaom --enable-libjxl --enable-libopenjpeg --enable-libvpx --enable-mediafoundation --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-liblensfun --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libvpl --enable-libshaderc --enable-vulkan --enable-libplacebo --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libilbc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
libavutil 58. 2.100 / 58. 2.100
libavcodec 60. 3.100 / 60. 3.100
libavformat 60. 3.100 / 60. 3.100
libavdevice 60. 1.100 / 60. 1.100
libavfilter 9. 3.100 / 9. 3.100
libswscale 7. 1.100 / 7. 1.100
libswresample 4. 10.100 / 4. 10.100
libpostproc 57. 1.100 / 57. 1.100
Input #0, matroska,webm, from '.\洞穴 burrow.mkv':
Metadata:
ENCODER : Lavf60.3.100
Duration: 00:05:19.02, start: 0.000000, bitrate: 1756 kb/s
Stream #0:0: Video: h264 (High), yuv420p(tv, bt709, progressive), 1920x1080 [SAR 1:1 DAR 16:9], 29.97 fps, 29.97 tbr, 1k tbn
Metadata:
HANDLER_NAME : ISO Media file produced by Google Inc.
VENDOR_ID : [0][0][0][0]
DURATION : 00:05:18.985000000
Stream #0:1(eng): Audio: opus, 48000 Hz, stereo, fltp
Metadata:
DURATION : 00:05:19.016000000

```

有什么方式能确定输出是走的 stdout 还是 stderr ,或者其他 stream 么?

查了官方文档,没找到相关内容。
xinghen57
149 天前
@matepi #1
```powershell
function Write-Messages
{

Write-Host "Host message"
Write-Output "Output message"
Write-Verbose "Verbose message"
Write-Warning "Warning message"
Write-Error "Error message"
Write-Debug "Debug message"
}

Write-Messages

# 输出结果如下

Host Messages
Output Messages
WARNING: Warning Messages
Write-Messages: Error Messages
```

上面的代码,可以看到,终端显示的输出,可能是 stdout ,也可能是 stderr ,还可能是 warning 。

所以如果在终端看到一段输出结果,怎么确定是 stdout ,或 stderr ,还是 warning ?
wangtian2020
149 天前
const { exec } = require('child_process');

// 要执行的 PowerShell 命令
const powershellCommand = 'Get-Process'; // 这里用一个简单的示例命令

// 执行 PowerShell 命令
const child = exec(`powershell.exe -Command "${powershellCommand}"`, (error, stdout, stderr) => {
if (error) {
console.error(`Error: ${error.message}`);
return;
}
if (stderr) {
console.error(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});

// 监听子进程关闭事件
child.on('close', (code) => {
console.log(`子进程退出,退出码 ${code}`);
});



用 nodejs 执行命令行,回调里面 stdout 和 stderr 是区分清楚的两个不同对象
ns09005264
149 天前
通过管道符导入到其他接受 stdin 的程序里试试,比如 vim 。像这样: ffmpeg command | vim ,vim 里没有输入就不是 stdin
Tumblr
149 天前
huangmiaomiao233
149 天前
重定向不就知道了,在末尾加 1>out.log 2>err.log ,可以只写其中一个,那么就只会显示另一个
Richex
148 天前
和 Linux 一样可以用 echo $?

[Imgur]( https://imgur.com/ClJK2sn)
xinghen57
148 天前
@wangtian2020 #4
@ns09005264 #5
@Tumblr #6
@huangmiaomiao233 #7
感谢诸位。最简单直接的是用重定向,就像 @huangmiaomiao233 #7 说的,也即靠经验和调试。

其实我向寻找的是类似 Get-Member 的命令,直接可以显示输出内容去了 stream x ,貌似没有这类命令。
xinghen57
148 天前
@Tumblr #6 这两篇我都看过,里面只是介绍了 stream 。
下面我找到的这篇有兴趣兄台可以看看,说的更深一些。

Understanding Streams, Redirection, and Write-Host in PowerShell
https://devblogs.microsoft.com/scripting/understanding-streams-redirection-and-write-host-in-powershell/
xinghen57
148 天前
@Richex #8 并不行的。

情况一:
```powershell

ffprobe -i 'video.mp4'

echo $?
```
此时 echo 输出 true ,ffprobe 输出实际走的 stderr 。

情况二:
```powershell

ffmpeg -i 'video.mp4'

echo $?
```
此时 echo 输出 false ,ffmpeg 输出实际走的 stderr 。

上述两种情况,powershell 内核的 windows terminal 都会显示 ffmpeg 或 ffprobe 的输出。
也即 echo $? 无法判断输出用的是 stream 几。
Richex
148 天前
看起来 ffmpeg 就是这么设计的吧,如果需要输出到 stdout 除了重定向也可以试着找一下官方提供的相关参数,例如:

ffprobe -i "in.mp4" -show_format > info.txt
ffprobe -i "in.mp4" -show_format -of json > info.json

从你的问题上来看,程序同时输出 stdout 和 stderr 是很平常的事情,所以如果你的问题是怎么确认终端输出是 stdout 还是 stderr 的话,个人感觉这个问题不太成立。

但其实绝大部分程序都会遵守 exit code ,即 0 是正常执行完成,非 0 异常,实际测试 ffmpeg 也是这样,
所以如果只是想知道是否执行成功其实判断 exit code 就足够了。而程序执行成功后和失败后的输出也是可预期的,判断一下 exit code 再从两个输出中根据需要读取就好了。
xinghen57
148 天前
@Richex #12 感谢回答。
通常的逻辑确是你说的这样。通常 error 信息会有 error 提示。exit code 确是可以判断正常与否。
因此,我认为 exit code 0 的输出应该走 stdout stream 。

ffprobe -i 'video.mp4'

上面命令,exit code 0 ,但输出却走了 stderr 。

而你给出的命令

ffprobe -i "in.mp4" -show_format > info.txt

它的输出使用的是 stdout stream ,命令的 exit code 1 。

很令人费解。

也是因此,才会有帖子的疑问,想找有没有显示输出 stream 编号的命令。
Richex
148 天前
我的理解是 ffprobe -i 'video.mp4' 在终端输出的那些信息 ffmpeg 官方认为只是辅助信息或者说是调试信息,并不作为程序输出,所以 exit 0 并且 stdout 是空算是正常情况。

或者将 ffprobe 当成一个“在终端'查看'文件信息”的工具,看起来好理解一点,在我看来这是 ffmpeg 的一种“选择”,自然也没有对错之分了。当然关于怎么输出应该交给官方了,我们在这讨论也起不到啥作用。

在我这里 ffprobe -i "in.mp4" -show_format > info.txt 命令的 exit code 为 0 ,你是说在你那边是 1 吗?

目前我还不是太理解你想要解决的问题,方便的话可以描述一下细节,或者“确定终端输出的 stream 号”是基于什么确定?

比如这个问题:如果在终端看到一段输出结果,怎么确定是 stdout ,或 stderr ,还是 warning ?

ffmpeg 已经将对应信息分别输出到 stdout, stderr 和 warning 这是已经确定的,应该怎么理解“怎么确定”?

我感觉 OP 目的好像是想要从 stderr 中读取信息的意思?
xinghen57
148 天前
@Richex #14 exit code 为 0 ,我打错了。

> 我的理解是 ffprobe -i 'video.mp4' 在终端输出的那些信息 ffmpeg 官方认为只是辅助信息或者说是调试信息,并不作为程序输出,所以 exit 0 并且 stdout 是空算是正常情况。

你说的这种思路,我特意用 yt-dlp 对比了一下。还真是这样。

yt-dlp 中有一条输出:
“WARNING: [youtube] Preferring "zh-CN" translated fields. Note that some metadata extraction may fail or be incorrect.”
这条输出字面上应属于 warning ,但实际却被输出到 stderr ,在 powershell output stream 的编号是 2 。

ffprobe 也是类似的“选择”,姑且认为是 ffmpeg 的选择。如果说是辅助或调试信息,放 information stream 或 debug stream 中可能更不容易让人迷惑。毕竟 powershell 提供了 6 条 stream ,其中就有 information stream (编号 6 ) 和 debug stream (编号 5 )。

我不太确定 bash 中是否只有 stdin 、stdout 和 stderr 。如果只有这 3 个,这么设计也没问题,但如果和 powershell 一样有多个 output stream ,辅助调试信息放 stderr 就容易让不熟悉的人迷惑。

ffprobe 的标准格式是 “ ffprobe [OPTIONS] INPUT_FILE”。
我当时默认 “ffprobe -i 'video.mp4'” 输出应该放到 stdout ,打算从其输出提取信息却遇到无法提取情况,最后发现原因是输出被放到了 stderr 。因此,产生了是否有现成命令识别 output stream 编号的想法。
Richex
148 天前
@xinghen57 明白了

我猜测是 ffmpeg 没有对 PowerShell 做处理,更多的还是从 Linux 平台出发做的。

yt-dlp 的代码里面 WARNING 也确定是直接输出到 stderr:

https://github.com/yt-dlp/yt-dlp/blob/d5d1517e7d838500800d193ac3234b06e89654cd/yt_dlp/YoutubeDL.py#L1032

没有考虑 PowerShell 的 warning 等通道:

https://github.com/yt-dlp/yt-dlp/blob/d5d1517e7d838500800d193ac3234b06e89654cd/yt_dlp/YoutubeDL.py#L609-L611
xinghen57
148 天前
@Richex #16 刚才的交流之后,也有类似的猜测。总是,再次感谢。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/999636

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX