最近 powershell 中用 ffmpeg ,发现它终端的输出竟然走的 stderr 。
请问 v 友,有方法确认终端输出是 stdout 还是 stderr 吗?或确定终端输出的 stream 号?
1
matepi 2023-12-12 11:46:20 +08:00 1
你是否需要
2>&1 把 stderr 输出重定向到 stdout ? 如果是程序环境内调用,是否考虑加一层 shell 环境来调用? cmd /C 或者 sh -c |
2
xinghen57 OP @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 么? 查了官方文档,没找到相关内容。 |
3
xinghen57 OP @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 ? |
4
wangtian2020 2023-12-12 14:56:49 +08:00
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 是区分清楚的两个不同对象 |
5
ns09005264 2023-12-12 14:58:40 +08:00
通过管道符导入到其他接受 stdin 的程序里试试,比如 vim 。像这样: ffmpeg command | vim ,vim 里没有输入就不是 stdin
|
6
Tumblr 2023-12-12 15:04:29 +08:00
|
7
huangmiaomiao233 2023-12-12 15:36:53 +08:00
重定向不就知道了,在末尾加 1>out.log 2>err.log ,可以只写其中一个,那么就只会显示另一个
|
8
Richex 2023-12-12 20:33:41 +08:00 1
|
9
xinghen57 OP @wangtian2020 #4
@ns09005264 #5 @Tumblr #6 @huangmiaomiao233 #7 感谢诸位。最简单直接的是用重定向,就像 @huangmiaomiao233 #7 说的,也即靠经验和调试。 其实我向寻找的是类似 Get-Member 的命令,直接可以显示输出内容去了 stream x ,貌似没有这类命令。 |
10
xinghen57 OP @Tumblr #6 这两篇我都看过,里面只是介绍了 stream 。
下面我找到的这篇有兴趣兄台可以看看,说的更深一些。 Understanding Streams, Redirection, and Write-Host in PowerShell https://devblogs.microsoft.com/scripting/understanding-streams-redirection-and-write-host-in-powershell/ |
11
xinghen57 OP @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 几。 |
12
Richex 2023-12-12 21:12:18 +08:00
看起来 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 再从两个输出中根据需要读取就好了。 |
13
xinghen57 OP @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 编号的命令。 |
14
Richex 2023-12-12 22:33:57 +08:00
我的理解是 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 中读取信息的意思? |
15
xinghen57 OP @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 编号的想法。 |
16
Richex 2023-12-12 23:25:14 +08:00
@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 |