V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xinghen57
V2EX  ›  PowerShell

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

  •  
  •   xinghen57 · 324 天前 · 1832 次点击
    这是一个创建于 324 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

    17 条回复    2023-12-12 23:31:26 +08:00
    matepi
        1
    matepi  
       324 天前   ❤️ 1
    你是否需要
    2>&1
    把 stderr 输出重定向到 stdout ?

    如果是程序环境内调用,是否考虑加一层 shell 环境来调用? cmd /C 或者 sh -c
    xinghen57
        2
    xinghen57  
    OP
       324 天前
    @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
        3
    xinghen57  
    OP
       324 天前
    @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
        4
    wangtian2020  
       324 天前
    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
        5
    ns09005264  
       324 天前
    通过管道符导入到其他接受 stdin 的程序里试试,比如 vim 。像这样: ffmpeg command | vim ,vim 里没有输入就不是 stdin
    huangmiaomiao233
        7
    huangmiaomiao233  
       324 天前
    重定向不就知道了,在末尾加 1>out.log 2>err.log ,可以只写其中一个,那么就只会显示另一个
    Richex
        8
    Richex  
       324 天前   ❤️ 1
    和 Linux 一样可以用 echo $?

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

    其实我向寻找的是类似 Get-Member 的命令,直接可以显示输出内容去了 stream x ,貌似没有这类命令。
    xinghen57
        10
    xinghen57  
    OP
       324 天前
    @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
        11
    xinghen57  
    OP
       324 天前
    @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
        12
    Richex  
       324 天前
    看起来 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
        13
    xinghen57  
    OP
       324 天前
    @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
        14
    Richex  
       324 天前
    我的理解是 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
        15
    xinghen57  
    OP
       324 天前
    @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
        16
    Richex  
       324 天前
    @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
        17
    xinghen57  
    OP
       324 天前
    @Richex #16 刚才的交流之后,也有类似的猜测。总是,再次感谢。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1292 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 23:25 · PVG 07:25 · LAX 16:25 · JFK 19:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.