Linux – How to determine the actual command that is piping into you

bashlinuxpipe

Let's say I have a bash script called log.sh. In this script, I want to read in input from a pipe, but I also want to know the command used to pipe input into me. Example:

tail -f /var/log/httpd/error | log.sh

In the shell script, I want to know the command tail -f /var/log/httpd/error.

Best Answer

Akira suggested using lsof.

Here's how you could script it:

whatpipe2.sh

#!/bin/bash

pid=$$
pgid=$(ps -o pgid= -p $pid)
lsofout=$(lsof -g $pgid)
pipenode=$(echo "$lsofout" | awk '$5 == "0r" { print $9 }')
otherpids=$(echo "$lsofout" | awk '$5 == "1w" { print $2 }')
for pid in $otherpids; do
    if cmd=$(ps -o cmd= -p $pid 2>/dev/null); then
        echo "$cmd"
        break
    fi
done

Running it:

$ tail -f /var/log/messages | ./whatpipe2.sh
tail -f /var/log/messages
^C

Another way is using process groups.

whatpipe1.sh

#!/bin/bash    

pid=$$
# ps output is nasty, can (and usually does) start with spaces
# to handle this, I don't quote the "if test $_pgrp = $pgrp" line below
pgrp=$(ps -o pgrp= -p $pid)
psout=$(ps -o pgrp= -o pid= -o cmd=)
echo "$psout" | while read _pgrp _pid _cmd; do
    if test $_pgrp = $pgrp; then
        if test $_pid != $pid; then
            case $_cmd in
            ps*)
                # don't print the "ps" we ran to get this info
                # XXX but this actually means we exclude any "ps" command :-(
                ;;
            *)
                echo "$_cmd"
                ;;
            esac
        fi
    fi
done

Running it:

$ tail -f /var/log/messages | ./whatpipe1.sh
tail -f /var/log/messages
^C

Note they both only work if the command on the left side of the pipe runs for long enough for ps to see it. You said you were using it with tail -f, so I doubt this is an issue.

$ sleep 0 | ./whatpipe1.sh 

$ sleep 1 | ./whatpipe1.sh
sleep 1
Related Question