Check output device is a terminal
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
Demonstrate how to check whether the output device is a terminal or not.
- Related task
Ada
We use the interface to C library functions isatty()
and fileno()
.
<lang ada>with Ada.Text_IO; use Ada.Text_IO; with Interfaces.C_Streams; use Interfaces.C_Streams;
procedure Test_tty is begin
if Isatty(Fileno(Stdout)) = 0 then Put_Line(Standard_Error, "stdout is not a tty."); else Put_Line(Standard_Error, "stdout is a tty."); end if;
end Test_tty;</lang>
- Output:
$ ./test_tty stdout is a tty. $ ./test_tty > /dev/null stdout is not a tty.
C
Use isatty()
on file descriptor to determine if it's a TTY. To get the file descriptor from a FILE*
pointer, use fileno
:
<lang c>#include <unistd.h> // for isatty()
- include <stdio.h> // for fileno()
int main() {
puts(isatty(fileno(stdout)) ? "stdout is tty" : "stdout is not tty"); return 0;
}</lang>
- Output:
$ ./a.out stdout is tty $ ./a.out > tmp $ cat tmp stdout is not tty $ ./a.out | cat stdout is not tty
C#
<lang csharp>using System;
namespace CheckTerminal {
class Program { static void Main(string[] args) { Console.WriteLine("Stdout is tty: {0}", Console.IsOutputRedirected); } }
}</lang>
C++
<lang cpp>#if _WIN32
- include <io.h>
- define ISATTY _isatty
- define FILENO _fileno
- else
- include <unistd.h>
- define ISATTY isatty
- define FILENO fileno
- endif
- include <iostream>
int main() {
if (ISATTY(FILENO(stdout))) { std::cout << "stdout is a tty\n"; } else { std::cout << "stdout is not a tty\n"; }
return 0;
}</lang>
COBOL
Works with GnuCOBOL.
<lang cobol> *>
*> istty, check id fd 0 is a tty *> Tectonics: cobc -xj istty.cob *> echo "test" | ./istty *> identification division. program-id. istty.
data division. working-storage section. 01 rc usage binary-long.
procedure division. sample-main.
call "isatty" using by value 0 returning rc display "fd 0 tty: " rc
call "isatty" using by value 1 returning rc display "fd 1 tty: " rc upon syserr
call "isatty" using by value 2 returning rc display "fd 2 tty: " rc
goback. end program istty.</lang>
DISPLAY for fd 1 is directed to SYSERR to get some output during the various trials.
- Output:
prompt$ cobc -xj istty.cob fd 0 tty: +0000000001 fd 1 tty: +0000000001 fd 2 tty: +0000000001 prompt$ echo "test" | ./istty fd 0 tty: +0000000000 fd 1 tty: +0000000001 fd 2 tty: +0000000001 prompt$ echo "test" | ./istty >/dev/null fd 1 tty: +0000000000 prompt$ echo "test" | ./istty 2>/dev/tty fd 0 tty: +0000000000 fd 1 tty: +0000000001 fd 2 tty: +0000000001 prompt$ echo "test" | ./istty 2>/dev/null fd 0 tty: +0000000000 fd 2 tty: +0000000000
Common Lisp
<lang lisp>(with-open-stream (s *standard-output*)
(format T "stdout is~:[ not~;~] a terminal~%" (interactive-stream-p s)))</lang>
- Output:
$ sbcl --script rc.lisp stdout is a terminal $ sbcl --script rc.lisp | cat stdout is not a terminal $ sbcl --script rc.lisp > foo.txt $ cat foo.txt stdout is not a terminal
We use the interface to C library functions isatty()
and fileno()
. It needs to be compiled to be executed.
<lang lisp>(ffi:clines "
#include <sys/ioctl.h> #include <unistd.h> int ttyPredicate() { return isatty(fileno(stdout)); }")
(ffi:def-function
("ttyPredicate" c-ttyp) () :returning :int)
(defun tty-p()
(if (= 1 (c-ttyp)) t nil))
(format T "stdout is~:[ not~;~] a terminal~%" (tty-p)) (quit)</lang>
Compilation can be done with the following commands :
ecl --eval '(compile-file "file.lisp" :system-p t)' --eval '(quit)'
ecl --eval '(c:build-program "is-tty" :lisp-files (list "file.o"))' --eval '(quit)'
- Output:
$ ./is-tty stdout is a terminal $ ./is-tty | cat - stdout is not a terminal
Crystal
<lang ruby>File.new("testfile").tty? #=> false File.new("/dev/tty").tty? #=> true STDOUT.tty? #=> true</lang>
D
<lang D>import std.stdio;
extern(C) int isatty(int);
void main() {
writeln("Stdout is tty: ", stdout.fileno.isatty == 1);
}</lang>
- Output:
prompt>a.out Stdout is tty: true prompt>a.out > out.txt Stdout is tty: false
Factor
You have to know 1 is the correct file descriptor number: <lang factor> IN: scratchpad USE: unix.ffi IN: scratchpad 1 isatty
--- Data stack: 1 </lang>
Go
Tells a terminal apart from a pipe on Linux and Mac, which is probably exactly what you need.
<lang go>package main
import (
"os" "fmt"
)
func main() {
if fileInfo, _ := os.Stdout.Stat(); (fileInfo.Mode() & os.ModeCharDevice) != 0 { fmt.Println("Hello terminal") } else { fmt.Println("Who are you? You're not a terminal") }
}</lang>
- Output:
> hello Hello terminal > hello | cat Who are you? You're not a terminal.
Haskell
<lang haskell>module Main where
-- requires the unix package -- https://hackage.haskell.org/package/unix import System.Posix.Terminal (queryTerminal) import System.Posix.IO (stdOutput)
main :: IO () main = do
istty <- queryTerminal stdOutput putStrLn (if istty then "stdout is tty" else "stdout is not tty")</lang>
- Output:
$ runhaskell istty.hs stdout is tty $ runhaskell istty.hs | cat stdout is not tty
J
<lang J>3=nc<'wd'</lang>
Explanation:
J does not have a concept of an "output device", so we approximate that by seeing whether we have bothered to define a the code which typically does graphical output.
The use of the phrase "output device" suggests that we are thinking about something like the unix `isatty` command. Here, stdout might be a file or might be a terminal. But in J we are often hosting our own user interaction environment. It's not uncommon for a J user to be on a web page where hitting enter sends a form request to a J interpreter which in turn composes an updated html presentation of current state which it sends to the browser. Or, the J user might be talking to a Java program which similarly wraps the J session (though this is older technology at this point). Or, the J user might be interacting with Qt. Or, sure, we might be talking to a tty and J might be sending its output straight to the tty. (But we can't know if that tty is hosted in emacs, running under control of a script on a remote machine via ssh, talking directly to a human user who happens to be in direct control of the session, or whatever else...)
The point being that in the general case the J programmer cannot know whether the concept of "terminal" has any relevance to the user.
But, like everyone else, we can certainly use heuristics.
But, correctness requires us to keep in mind that these will only be heuristics, and will sometimes be incorrect (hopefully not often enough to matter a lot...).
Javascript/NodeJS
<lang js>node -p -e "Boolean(process.stdout.isTTY)" true</lang>
Julia
<lang Julia> if isa(STDOUT, Base.TTY)
println("This program sees STDOUT as a TTY.")
else
println("This program does not see STDOUT as a TTY.")
end </lang>
- Output:
This program sees STDOUT as a TTY.
Kotlin
<lang scala>// Kotlin Native version 0.5
import platform.posix.*
fun main(args: Array<String>) {
if (isatty(STDOUT_FILENO) != 0) println("stdout is a terminal") else println("stdout is not a terminal")
}</lang>
- Output:
stdout is a terminal
Lua
Using pure Lua, assuming a *NIX-like runtime environment ... <lang Lua>local function isTTY ( fd )
fd = tonumber( fd ) or 1 local ok, exit, signal = os.execute( string.format( "test -t %d", fd ) ) return (ok and exit == "exit") and signal == 0 or false
end
print( "stdin", isTTY( 0 ) ) print( "stdout", isTTY( 1 ) ) print( "stderr", isTTY( 2 ) )</lang>
- Output:
$ lua istty.lua stdin true stdout true stderr true $ cat /dev/null | lua istty.lua stdin false stdout true stderr true $ lua istty.lua | tee stdin true stdout false stderr true $ lua istty.lua 2>&1 | tee stdin true stdout false stderr false
You can accomplish the same results using the luaposix [1] library: <lang lua>local unistd = require( "posix.unistd" )
local function isTTY ( fd )
fd = tonumber( fd ) or 1 local ok, err, errno = unistd.isatty( fd ) return ok and true or false
end
print( "stdin", isTTY( 0 ) ) print( "stdout", isTTY( 1 ) ) print( "stderr", isTTY( 2 ) )</lang>
The output of this version is identical to the output of the first version.
Nemerle
There is no explicit way (ie isatty())to do this; however, if we assume that standard out is a terminal, we can check if the output stream has been redirected (presumably to something other than a terminal). <lang Nemerle>def isTerm = System.Console.IsOutputRedirected;</lang>
OCaml
<lang ocaml>let () =
print_endline ( if Unix.isatty Unix.stdout then "Output goes to tty." else "Output doesn't go to tty." )</lang>
Testing in interpreted mode:
$ ocaml unix.cma istty.ml Output goes to tty. $ ocaml unix.cma istty.ml > tmp $ cat tmp Output doesn't go to tty. $ ocaml unix.cma istty.ml | cat Output doesn't go to tty.
Perl
The -t function on a filehandle tells you whether it's a terminal.
<lang bash>$ perl -e "warn -t STDOUT ? 'Terminal' : 'Other'" Terminal $ perl -e "warn -t STDOUT ? 'Terminal' : 'Other'" > x.tmp Other </lang>
Phix
Requires 0.8.2+ <lang Phix>printf(1,"stdin:%t, stdout:%t, stderr:%t\n",{isatty(0),isatty(1),isatty(2)})</lang>
- Output:
C:\Program Files (x86)\Phix>p test stdin:true, stdout:true, stderr:true C:\Program Files (x86)\Phix>p test > test.txt; type test.txt stdin:true, stdout:false, stderr:true C:\Program Files (x86)\Phix>p test 2> test.txt stdin:true, stdout:true, stderr:false
PHP
<lang php> if(posix_isatty(STDOUT)) {
echo "The output device is a terminal".PHP_EOL;
} else {
echo "The output device is NOT a terminal".PHP_EOL;
} </lang>
Python
Pretty much the same as Check input device is a terminal#Python. <lang python>from sys import stdout if stdout.isatty():
print 'The output device is a teletype. Or something like a teletype.'
else:
print 'The output device isn\'t like a teletype.'</lang>
Racket
<lang racket> (terminal-port? (current-output-port)) </lang>
Raku
(formerly Perl 6)
The .t method on a filehandle tells you whether it's going to the terminal. Here we use the note function to emit our result to standard error rather than standard out.
$ raku -e 'note $*OUT.t' True $ raku -e 'note $*OUT.t' >/dev/null False
REXX
Programming note: The comment about the REXX statements have to be on one line isn't quite true,
but because the REXX special variable SIGL is defined where it's executed, it makes coding simpler.
SIGL is set to the REXX statment number where:
- a CALL statement is used
- a function is invoked
- a SIGNAL statement is used
Method used: since REXX has no direct way of determining if the STDIN is a terminal or not, the REXX code (below)
actually raises (which is no way to run a railroad) a syntax error when attempting to read the 2nd line from STDIN,
which causes a routine (named syntax:) to get control, determines where the syntax error occurred, and returns an
appropriate string indicating if STDIN is a terminal (or other).
Note that under VM/CMS, this can be accomplished with a (host) command within REXX and then examining the results.
On IBM mainframes, a user can have STDIN defined, but the terminal can be disconnected.
<lang rexx>/*REXX program determines if the STDIN is a terminal or other. */
signal on syntax /*if syntax error, jump──► SYNTAX*/
say 'output device:' testSTDIN() /*displays terminal ──or── other */
exit /*stick a fork in it, we're done.*/
/*──────────────────────────────────TESTSTDIN subroutine────────────────*/
testSTDIN: syntax.=1; signal .; .: z.=sigl; call linein ,2; ..: syntax.=0
return z.. /* [↑] must all be on one line.*/
/*──────────────────────────────────SYNTAX subroutine───────────────────*/
syntax: z..='other' /*when SYNTAX occur, come here. */
if syntax. then do /*handling STDIN thingy error? */
if sigl==z. then z..='terminal'; signal .. /*stdin ?*/ end /* [↑] can't use a RETURN here.*/
/* ··· handle other REXX syntax errors here ··· */</lang>
output
output device: terminal
Ruby
<lang rust>f = File.open("test.txt") p f.isatty # => false p STDOUT.isatty # => true </lang>
Rust
<lang rust>/* Uses C library interface */
extern crate libc;
fn main() {
let istty = unsafe { libc::isatty(libc::STDOUT_FILENO as i32) } != 0; if istty { println!("stdout is tty"); } else { println!("stdout is not tty"); }
}</lang>
Scala
<lang scala>import org.fusesource.jansi.internal.CLibrary._
object IsATty extends App {
var enabled = true
def apply(enabled: Boolean): Boolean = { // We must be on some unix variant.. try { enabled && isatty(STDOUT_FILENO) == 1 } catch { case ignore: Throwable => ignore.printStackTrace() false
} }
println("tty " + apply(true))
}</lang>
Tcl
To detect whether output is going to a terminal in Tcl, you check whether the stdout
channel looks like a serial line (as those are indistinguishable from terminals). The simplest way of doing that is to see whether you can read the -mode or -xchar
channel options, which are only present on serial channels:
<lang tcl>set toTTY [dict exists [fconfigure stdout] -mode]
puts [expr {$toTTY ? "Output goes to tty" : "Output doesn't go to tty"}]</lang>
At the system call level, when Tcl is setting up the channels that correspond to the underlying stdout (and stdin and stderr) file descriptors, it checks whether the channels are network sockets (with getsockname()
) or serial lines (with isatty()
). This allows Tcl scripts to find out information about their calling environment (e.g., when they are run from inetd) with minimal code.
- Demonstrating:
Assuming that the above script is stored in the file istty.tcl:
$ tclsh8.5 istty.tcl Output goes to tty $ tclsh8.5 istty.tcl | cat Output doesn't go to tty
Channel type discovery with older Tcl versions
Before Tcl 8.4, this discovery process is impossible; stdout
always looks like it is going to a file. With 8.4, you can discover the channel type but you need slightly different (and less efficient, due to the thrown error in the non-tty case) code to do it.
<lang tcl>set toTTY [expr {![catch {fconfigure stdout -mode}]}]</lang>
UNIX Shell
<lang sh>#!/bin/sh
if [ -t 1 ] then
echo "Output is a terminal"
else
echo "Output is NOT a terminal" >/dev/tty
fi</lang>
Visual Basic .NET
<lang vbnet>Module Module1
Sub Main() Console.WriteLine("Stdout is tty: {0}", Console.IsOutputRedirected) End Sub
End Module</lang>
zkl
On Unix, check to see if stdout's st_mode is a character device. <lang zkl>const S_IFCHR=0x2000; fcn S_ISCHR(f){ f.info()[4].bitAnd(S_IFCHR).toBool() } S_ISCHR(File.stdout).println();</lang>
- Output:
$ zkl bbb # from the command line True $ zkl bbb | more False $ zkl bbb > foo.txt $ cat foo.txt False