写入STDOUT和打开到“/dev/tty”的文件句柄有什么区别?

10

这两个示例之间有什么区别?

#!/usr/bin/perl
use warnings;
use 5.012;
my $str = "\x{263a}";


open my $tty, '>:encoding(utf8)', '/dev/tty' or die $!;
say $tty $str;
close $tty;

open $tty, '>:bytes', '/dev/tty' or die $!;
say $tty $str;
close $tty;

# -------------------------------------------------------

binmode STDOUT, ':encoding(utf8)' or die $!;
say $str;

binmode STDOUT, ':bytes' or die $!;
say $str;

2
其中一个写入/dev/tty,另一个写入STDOUT。这是你想知道的吗? - JB.
@JB - 对于不太熟悉Unix的人来说,这不是一个琐碎的区别。 - DVK
@DVK 你说得对。当问题的主旨是Unix时,我被Perl的方向所困惑了。感谢您的启示! - JB.
@JB - 哎呀!我是启蒙的源泉!现在我得弄清楚我是否发光。 - DVK
4个回答

14
区别在于您正在写入两个不同的文件句柄,这些文件句柄是从Perl和您的程序的角度来看互相独立的。
  • 第一个是打开到Unixy OS上特殊的"设备"文件的文件句柄,它是"进程控制终端的代名词(如果有的话)"(引用自this Linux document)。请注意,虽然它通常被认为是"屏幕",但它不必是这样的(例如,该终端可以链接到串行端口的设备文件),并且它可能不存在或无法打开。
  • 第二个是默认与进程的文件描述符#1相关联的文件句柄。
由于在典型情况下,Unix shell会默认将其文件描述符#1(因此每个没有重定向的进程都会使用)与/dev/tty关联起来,所以它们乍一看似乎是相同的。
除了Unix shell的工作方式会导致这两者通常默认关联之外,从Perl的角度来看,这两者没有任何共同之处。
这两个引用代码的功能行为通常似乎相同,但这只是"偶然"现象。
其中的实际差异包括:
  • /dev/tty在非Unix系统上并不一定存在。因此,使用tty是高度不可移植的。Windows等价物是CON:(如果我没记错的话)。

  • 程序的STDOUT可以被调用程序的任何人关联(重定向)到任何地方。它可以关联到文件,也可以是另一个进程的STDIN


您可以使用-t运算符检查您的STDOUT是否连接到tty:

if ( -t STDOUT ) { say 'STDOUT is connected to a tty' }

作为另一个方面的说明,请注意您可以通过显式关闭STDOUT文件句柄,然后重新打开它以指向/dev/tty来确保STDOUT写入/dev/tty:
close STDOUT or die $!;
open STDOUT '>:encoding(utf8)', '/dev/tty' or die $!;

2
请注意,很多情况下根本无法使用 /dev/tty(因为不存在控制终端)- 一个常见的例子是通过 ssh 调用的命令 ssh somehost somecommand - ssh 默认会连接 STDIN、STDOUT 和 STDERR,但不会创建实际的终端。 - bdonlan
@bdolan - 正确的(因此在我引用的定义中添加了“如果有”的内容)。或者,举个简单的例子,在Windows上 :) - DVK
@JB - 感谢您的 -t 编辑!顺便提一下,如果我没记错的话,它无法区分 /dev/tty 或任何其他终端设备。 - DVK
很抱歉,可移植性真是个麻烦事,不是吗?但是,就我个人而言,如果有人能够为我提供一个与进程控制终端不同的tty连接的STDOUT,我会认为他知道自己在做什么,然后就让它过去了。 - JB.

4

从交互式shell启动的程序通常会将标准输出写入终端,这将使/dev/ttySTDOUT成为相同的目的地。但有几种情况下,输出到STDOUT可能会写入到其他目的地。

STDOUT可能被路由到单独的文件中:

perl someprogram.pl > a/file
perl someprogram.pl >> a/file

STDOUT 可以被路由到另一个程序的输入

perl someprogram.pl | /usr/bin/mailx -s "Program Output" foo@bar.com

此外,该程序可以从非交互式shell(如cron作业或在系统上运行的其他守护程序)启动。这些程序的环境将无法访问/dev/tty设备,并且这些程序中的STDOUT将被路由到其他位置(或不显示)。

4
除了DVK所说的,你可以通过以下方式简单了解它们的区别:

perl -le 'open $o, ">:encoding(utf8)", "/dev/tty"; print "STDOUT"; print $o "/dev/tty"' > /dev/null

写入STDOUT的内容会被重定向到/dev/null,但是写入$o的内容会显示在屏幕上。


0

1. stdout、stderr、stdin是什么? 它们是fd/0、fd/1、fd/2的别名

root@192-168-31-33:~# ls -alh /dev/std*
lrwxrwxrwx 1 root root 15 Apr 10 06:35 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 Apr 10 06:35 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 Apr 10 06:35 /dev/stdout -> /proc/self/fd/1

root@192-168-31-33:~# echo hello > /proc/self/fd/1
hello

2. /dev/console是什么?/dev/console指向tty1或ttyS0。
tty

3. /dev/std{out, in, err}和tty*设备之间有什么关系?/dev/std{out, in, err}设备是tty*设备的包装器。

#include <unistd.h>
#include <stdio.h>
void print_tty(char* name, FILE * f) {
  printf("%s (fileno %d): ", name, fileno(f));
  if (isatty(fileno(f))) printf("TTY %s\n", ttyname(fileno(f)));
  else                   printf("not a TTY\n");
}
int main(void) {
  print_tty("stdin ", stdin);
  print_tty("stdout", stdout);
  print_tty("stderr", stderr);
}

在此输入图片描述 或者,一个更简单的例子: 在此输入图片描述


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接