bashshellzsh

Testing whether stdin is a file vs. a pipe vs. a tty


I know for bash and zsh, one can use e.g. [ -t 1 ] to determine if STDIN is an interactive tty session.

However, there doesn't seem to be a way to test whether stdin is being redirected from a file, versus being piped in from a command:

foo < ./file
bar | foo 

Is there any way to detect the difference between these two? Separately, is there any way to get the path of the file being redirected from (outside of /proc/self, which is unavailable on macOS)?


Solution

  • You can check if /dev/stdin is a regular file or a pipe:

    $ cat tmp.sh
    #!/bin/bash
    
    if [ -f /dev/stdin ]; then
        echo "file"
    elif [ -p /dev/stdin ]; then
        echo "pipe"
    fi
    $ bash tmp.sh < foo.txt
    file
    $ echo foo | bash tmp.sh
    pipe
    

    This relies on /dev/stdin being in your file system, though.

    You can also use the stat command, which will return information about standard input given no file name argument. As you mentioned you are using macOS, you can use the %HT format:

    $ stat -f %HT
    Character Device
    $ stat -f %HT < foo.txt
    Regular File
    $ echo foo | stat -f %HT
    Fifo File