10

What are the differences between this two examples?

#!/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;
jeha
  • 10,562
  • 5
  • 50
  • 69
sid_com
  • 24,137
  • 26
  • 96
  • 187
  • 2
    One of them writes to /dev/tty, one of them writes to STDOUT. Was that what you wanted to know? – JB. Jan 12 '11 at 09:58
  • @JB - it's not a trivial distinction for people not terribly familiar with Unix – DVK Jan 12 '11 at 12:32
  • @DVK You're right. I was confused by the Perl orientation of the question when the gist of it was Unix. Thanks for the enlightenment! – JB. Jan 12 '11 at 12:36
  • @JB - Yay! I'm a source of enlightnment! Now I gotta figure out if I'm fluorescent. – DVK Jan 12 '11 at 12:43

4 Answers4

14

The difference is that you are writing to two distinct and (from Perl's and your program's point of view) independent file handles.

  • The first one is a file handle opened to a special "device" file on Unixy OS which is "a synonym for the controlling terminal of a process, if any" (quote from this Linux document). Please note that while it is commonly thought of as "screen", it doesn't have to be (e.g. that terminal could be linked to a serial port's device file instead); and it may not exist or not be openable.

  • The second one is a file handled associated by default with file descriptor #1 for the process.

They may SEEM to be identical at first glance due to the fact that, in a typical situation, a Unix shell will by default associate its file descriptor #1 (and thus one of every process it launches without redirects) with /dev/tty.

The two have nothing in common from Perl point of view, OTHER than the fact that those two commonly end up associated by default due to the way Unix shells work.

The functional behavior of the two quoted pieces of code will often SEEM identical due to this default, but that is just "by accident".

Among practical differences:

  • /dev/tty does not necessarily exist on non-Unixy OSs. It's therefore highly un-portable to use tty. Windows equivalent is CON: IIRC.

  • STDOUT of a program can be associated (re-directed) to ANYTHING by whoever called the program. Could be associated to a file, could be a pipe to another process's STDIN.


You can CHECK whether your STDOUT is connected to a tty by using the -t operator:

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

As another aside, please note that you CAN make sure that your STDOUT writes to /dev/tty by explicitly closing the STDOUT filehandle and re-opening it to point to /dev/tty:

close STDOUT or die $!;
open STDOUT '>:encoding(utf8)', '/dev/tty' or die $!;
DVK
  • 126,886
  • 32
  • 213
  • 327
  • 2
    Note also that there are plenty of cases where `/dev/tty` cannot be used at all (since there is no controlling terminal) - one common example is a command invoked via ssh as `ssh somehost somecommand` - ssh will, by default, hook up STDIN, STDOUT, and STDERR, but not create an actual terminal. – bdonlan Jan 12 '11 at 12:36
  • @bdolan - correct (thus the ", if any" added in the definition I quoted). Or, for a simpler example, on Windows :) – DVK Jan 12 '11 at 12:41
  • @JB - thanks for `-t` edit! Just as an aside, IIRC it can not distinguish between `/dev/tty` or any other terminal device. – DVK Jan 12 '11 at 12:47
  • I'm afraid not. Portability's a bitch, aint'it? Then again, IMHO, if someone managed to provide me with an STDOUT connected to a tty other than the process's controlling one, I'd assume he knew what he was doing and let it slide. – JB. Jan 12 '11 at 13:15
4

In addition to what DVK said, you can see the simple difference by saying

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

The write to STDOUT goes to /dev/null, but the write to $o goes to the screen.

Chas. Owens
  • 64,182
  • 22
  • 135
  • 226
4

A program launched from an interactive shell normally write standard output to a terminal, which would render /dev/tty and STDOUT as the same destination. But there are several circumstances where output to STDOUT could be written to some other destination.

STDOUT may be routed to a separate file:

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

STDOUT may be routed to the input of another program

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

Also, the program could be launched from a non-interactive shell, like a cron job or from some other daemon running on your system. The environments for these programs will not have access to a /dev/tty device, and STDOUT in these programs will be routed somewhere else (or nowhere).

mob
  • 117,087
  • 18
  • 149
  • 283
0

1. what stdout, stderr, stdin is? they are alias for 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. what /dev/console is? /dev/console is pointed to tty1 or ttyS0. tty

3.what relationship is between the /dev/std{out, in, err} and tty* devices? the devices /dev/std{out, in, err} are a wapper of tty* devices.

#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);
}

enter image description here or, a more simple example: enter image description here

jamlee
  • 1,234
  • 1
  • 13
  • 26