首页 > 解决方案 > ioctl 中应该使用哪个文件描述符来了解终端屏幕大小?

问题描述

即使应用程序在子外壳中运行,我也想可靠地知道终端大小。似乎唯一可行的方法是使用标准输入 fd,但我想知道为什么有指向证明它的文档的链接。有很多问题,比如为什么还要使用标准输出(或标准输入)fd 来了解终端大小?

fn main() {
    let mut size = winsize {
        ws_row: 0,
        ws_col: 0,
        ws_xpixel: 0,
        ws_ypixel: 0,
    };
    let fd = std::fs::OpenOptions::new()
        .read(true)
        .write(true)
        .open("/dev/tty")
        .map(|file| file.as_raw_fd())
        .unwrap()
        ;
    unsafe { ioctl(fd, TIOCGWINSZ.into(), &mut size) };
    println!("/dev/tty cols: {}, lines: {}", size.ws_row, size.ws_col);
    unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ.into(), &mut size) };
    println!("stdout cols: {}, lines: {}", size.ws_row, size.ws_col);
    unsafe { ioctl(STDERR_FILENO, TIOCGWINSZ.into(), &mut size) };
    println!("stderr cols: {}, lines: {}", size.ws_row, size.ws_col);
    unsafe { ioctl(STDIN_FILENO, TIOCGWINSZ.into(), &mut size) };
    println!("stdin cols: {}, lines: {}", size.ws_row, size.ws_col);
}

我跑进echo (cargo run 2>&1)鱼壳,得到了这个:

   Compiling test_term_size v0.1.0 (/home/m/code/test_term_size) warning: unused import: `File`  --> src/main.rs:2:15   | 2 | use std::fs::{File};   |               ^^^^   |   = note: `#[warn(unused_imports)]` on by default  warning: unused imports: `O_RDWR`, `open`  --> src/main.rs:4:5   | 4 |     O_RDWR,   |     ^^^^^^ 5 |     open, ioctl, winsize, STDIN_FILENO,   |     ^^^^      Finished dev [unoptimized + debuginfo] target(s) in 0.21s      Running `target/debug/test_term_size` /dev/tty cols: 0, lines: 0 stdout cols: 0, lines: 0 stderr cols: 0, lines: 0 stdin cols: 52, lines: 106
strace -e trace=ioctl,open target/debug/test_term_size
ioctl(3, TIOCGWINSZ, 0x7ffdb9805578)    = -1 EBADF (Bad file descriptor)
/dev/tty cols: 0, lines: 0
ioctl(1, TIOCGWINSZ, {ws_row=52, ws_col=213, ws_xpixel=3834, ws_ypixel=2028}) = 0
stdout cols: 52, lines: 213
ioctl(2, TIOCGWINSZ, {ws_row=52, ws_col=213, ws_xpixel=3834, ws_ypixel=2028}) = 0
stderr cols: 52, lines: 213
ioctl(0, TIOCGWINSZ, {ws_row=52, ws_col=213, ws_xpixel=3834, ws_ypixel=2028}) = 0
stdin cols: 52, lines: 213
+++ exited with 0 +++
strace -e trace=ioctl,open cargo run
ioctl(2, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(2, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(2, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(2, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(2, TIOCGWINSZ, {ws_row=52, ws_col=213, ws_xpixel=3834, ws_ypixel=2028}) = 0
ioctl(2, TIOCGWINSZ, {ws_row=52, ws_col=213, ws_xpixel=3834, ws_ypixel=2028}) = 0
ioctl(2, TIOCGWINSZ, {ws_row=52, ws_col=213, ws_xpixel=3834, ws_ypixel=2028}) = 0
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/test_term_size`
ioctl(3, TIOCGWINSZ, 0x7ffea819b008)    = -1 EBADF (Bad file descriptor)
/dev/tty cols: 0, lines: 0
ioctl(1, TIOCGWINSZ, {ws_row=52, ws_col=213, ws_xpixel=3834, ws_ypixel=2028}) = 0
stdout cols: 52, lines: 213
ioctl(2, TIOCGWINSZ, {ws_row=52, ws_col=213, ws_xpixel=3834, ws_ypixel=2028}) = 0
stderr cols: 52, lines: 213
ioctl(0, TIOCGWINSZ, {ws_row=52, ws_col=213, ws_xpixel=3834, ws_ypixel=2028}) = 0
stdin cols: 52, lines: 213
+++ exited with 0 +++
echo (strace -e trace=ioctl,open cargo run 2>&1)
ioctl(2, TCGETS, 0x7ffdae149cb0)        = -1 ENOTTY (Inappropriate ioctl for device) ioctl(2, TCGETS, 0x7ffdae149a30)        = -1 ENOTTY (Inappropriate ioctl for device) ioctl(2, TCGETS, 0x7ffdae149a30)        = -1 ENOTTY (Inappropriate ioctl for device) ioctl(2, TCGETS, 0x7ffdae149540)        = -1 ENOTTY (Inappropriate ioctl for device) warning: unused import: `File`  --> src/main.rs:2:15   | 2 | use std::fs::{File};   |               ^^^^   |   = note: `#[warn(unused_imports)]` on by default  warning: unused imports: `O_RDWR`, `open`  --> src/main.rs:4:5   | 4 |     O_RDWR,   |     ^^^^^^ 5 |     open, ioctl, winsize, STDIN_FILENO,   |     ^^^^      Finished dev [unoptimized + debuginfo] target(s) in 0.02s      Running `target/debug/test_term_size` ioctl(3, TIOCGWINSZ, 0x7ffd8c3ae728)    = -1 EBADF (Bad file descriptor) /dev/tty cols: 0, lines: 0 ioctl(1, TIOCGWINSZ, 0x7ffd8c3ae728)    = -1 ENOTTY (Inappropriate ioctl for device) stdout cols: 0, lines: 0 ioctl(2, TIOCGWINSZ, 0x7ffd8c3ae728)    = -1 ENOTTY (Inappropriate ioctl for device) stderr cols: 0, lines: 0 ioctl(0, TIOCGWINSZ, {ws_row=52, ws_col=213, ws_xpixel=3834, ws_ypixel=2028}) = 0 stdin cols: 52, lines: 213 +++ exited with 0 +++
echo (strace -e trace=ioctl,open target/debug/test_term_size 2>&1)
ioctl(3, TIOCGWINSZ, 0x7fffdc82e938)    = -1 EBADF (Bad file descriptor) /dev/tty cols: 0, lines: 0 ioctl(1, TIOCGWINSZ, 0x7fffdc82e938)    = -1 ENOTTY (Inappropriate ioctl for device) stdout cols: 0, lines: 0 ioctl(2, TIOCGWINSZ, 0x7fffdc82e938)    = -1 ENOTTY (Inappropriate ioctl for device) stderr cols: 0, lines: 0 ioctl(0, TIOCGWINSZ, {ws_row=52, ws_col=213, ws_xpixel=3834, ws_ypixel=2028}) = 0 stdin cols: 52, lines: 213 +++ exited with 0 +++
echo (strace -e trace=ioctl,open target/debug/test_term_size 2>&1)
ioctl(3, TIOCGWINSZ, 0x7fffdc82e938)    = -1 EBADF (Bad file descriptor) /dev/tty cols: 0, lines: 0 ioctl(1, TIOCGWINSZ, 0x7fffdc82e938)    = -1 ENOTTY (Inappropriate ioctl for device) stdout cols: 0, lines: 0 ioctl(2, TIOCGWINSZ, 0x7fffdc82e938)    = -1 ENOTTY (Inappropriate ioctl for device) stderr cols: 0, lines: 0 ioctl(0, TIOCGWINSZ, {ws_row=52, ws_col=213, ws_xpixel=3834, ws_ypixel=2028}) = 0 stdin cols: 52, lines: 213 +++ exited with 0 +++

标签: rustposixlibcioctl

解决方案


TTY 信息的正确来源始终是 /dev/tty。不要使用标准输入或任何其他奇怪的黑客。这就是为什么它看起来不适合你的原因:

let fd = std::fs::OpenOptions::new()
    .read(true)
    .write(true)
    .open("/dev/tty")
    .map(|file| file.as_raw_fd())
    .unwrap()
    ;

您正在打开 /dev/tty 并获取其 FD,但该文件随后被丢弃,因此在您可以使用它之前关闭,因此其上的 ioctl 因 EBADF“错误文件描述符”而失败。要解决此问题,请保持文件处于活动状态,直到您使用完 FD。


推荐阅读