Detecting the output stream type of a shell script

2019-03-24 15:39发布

问题:

I'm writing a shell script that uses ANSI color characters on the command line.

Example: example.sh

#!/bin/tcsh
printf "\033[31m Success Color is awesome!\033[0m"

My problem is when doing:

$ ./example.sh > out

or

$./example.sh | grep 

The ASCII codes will be sent out raw along with the text, mucking up the output and just generally causing chaos.

I'm interested to know if there is a way to detect this so I could disable color for this special case.

I've search the tcsh man pages and the web for a while and have not been able to find anything shell specific yet.

I'm not bound to tcsh, it's our group standard... but who cares?

Is it possible to detect, inside a shell script, if your output is being redirected or piped?

回答1:

See this previous SO question, which covers bash. Tcsh provides the same functionality with filetest -t 1 to see if standard output is a terminal. If it is, then print the color stuff, else leave it out. Here's tcsh:

#!/bin/tcsh
if ( -t 1 ) then
        printf "\033[31m Success Color is awesome!\033[0m"
else
        printf "Plain Text is awesome!"
endif


回答2:

Inside a bourne shell script (sh, bask, ksh, ...), you can feed the standard output to the tty program (standard in Unix) which tells you whether its input is a tty or not, by using the -s flag.

Put the following into "check-tty":

    #! /bin/sh
    if tty -s <&1; then
      echo "Output is a tty"
    else
      echo "Output is not a tty"
    fi

And try it:

    % ./check-tty
    Output is a tty
    % ./check-tty | cat
    Output is not a tty

I don't use tcsh, but there must be a way to redirect your standard output to tty's standard input. If not, use

    sh -c "tty -s <&1"

as your test command in your tcsh script, check its exit status and you're done.



回答3:

The detection of the output stream type is covered in the question detect if shell script is running through a pipe.

Having decided that you are talking to terminal, then you can use tput to retrieve the correct escape codes for the particular terminal you are using - this will make the code more portable.

An example script (in bash I am afraid, as tcsh is not my forte) is given below.

#!/bin/bash

fg_red=
fg_green=
fg_yellow=
fg_blue=
fg_magenta=
fg_cyan=
fg_white=
bold=
reverse=
attr_end=

if [ -t 1 ]; then
    fg_red=$(tput setaf 1)
    fg_green=$(tput setaf 2)
    fg_yellow=$(tput setaf 3)
    fg_blue=$(tput setaf 4)
    fg_magenta=$(tput setaf 5)
    fg_cyan=$(tput setaf 6)
    fg_white=$(tput setaf 7)
    bold=$(tput bold)
    reverse=$(tput rev)
    underline=$(tput smul)
    attr_end=$(tput sgr0)
fi

echo "This is ${fg_red}red${attr_end}"
echo "This is ${fg_green}green${attr_end}"
echo "This is ${fg_yellow}yellow${attr_end}"
echo "This is ${fg_blue}blue${attr_end}"
echo "This is ${fg_magenta}magenta${attr_end}"
echo "This is ${fg_cyan}cyan${attr_end}"
echo "This is ${fg_white}white${attr_end}"
echo "This is ${bold}bold${attr_end}"
echo "This is ${reverse}reverse${attr_end}"
echo "This is ${underline}underline${attr_end}"

For more information see "man tput" and "man terminfo" - there are all sorts of escape codes to play with.



回答4:

As far as I know, there is no way to determine the final destination of the output of your shell script; the only thing you can do is provide a switch which allows for suppression of control characters in the output.