rust - 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 +++
解决方案
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。
推荐阅读
- php - SimpleSAMLphp 未在注销时重定向
- javascript - JavaScript 中的多文件选择和处理
- forms - Vaadin 8:从 Java bean 创建表单
- c++ - 在运行时增加数组的大小
- html - 如果没有找到,Django 显示段落
- scala - Binding.scala 路由重定向到登录页面
- python - 保持文件句柄打开的缺点?
- php - 使用 PHP 在 Microsoft Azure 上创建路径
- java - 如何将练习插入到谷歌适合的名称、卡路里和持续时间
- sql-server - SQL-我需要将一列中的所有单词匹配到另一列中的所有单词,反之亦然以匹配它们