Is it possible to access the complete command line including pipes in a bash script?

no

bash (or your shell) will fork two distinct commands.

  1. test.sh arg1
  2. grep "xyz"

test.sh couldn't know about following grep.

you might however know you are "inside" a pipe by testing /proc/self/fd/1

test.sh

#!/bin/bash

file /proc/self/fd/1

which run as

> ./test.sh
/proc/self/fd/1: symbolic link to /dev/pts/0
> ./test.sh | cat
/proc/self/fd/1: broken symbolic link to pipe:[25544239]

(Edit) see muru’s comment about knowing if you are on a pipe.

you don't need to know if you're in a pipe for that. Just check if output is a TTY. [ -t 1 ] https://unix.stackexchange.com/a/401938/70524


There is no way to do that in general.

But an interactive bash shell can leverage the history mechanism and the DEBUG trap to "tell" the commands it runs the complete command line they're part of via an environment variable:

$ trap 'export LC=$(fc -nl -0); LC=${LC#? }' DEBUG
$ sh -c 'printf "last_command={%s}\n" "$LC"' | cat; true
last_command={sh -c 'printf "last_command={%s}\n" "$LC"' | cat; true}

By using /proc/self/fd, you can see if you're in a pipeline as well as an ID for the pipe. If you iterate through /proc/\*/fd looking for the matching pipe, you can find the PID of the other end of the pipe. With the PID, you can then read /proc/$PID/cmdline as well as repeat the process on its file descriptors to find what it's piped into.

$ cat | cat | cat &
$ ps
  PID TTY          TIME CMD
 6942 pts/16   00:00:00 cat
 6943 pts/16   00:00:00 cat
 6944 pts/16   00:00:00 cat
 7201 pts/16   00:00:00 ps
20925 pts/16   00:00:00 bash
$ ls -l /proc/6942/fd
lrwx------. 1 tim tim 64 Jul 24 19:59 0 -> /dev/pts/16
l-wx------. 1 tim tim 64 Jul 24 19:59 1 -> 'pipe:[49581130]'
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16
$ ls -l /proc/6943/fd
lr-x------. 1 tim tim 64 Jul 24 19:59 0 -> 'pipe:[49581130]'
l-wx------. 1 tim tim 64 Jul 24 19:59 1 -> 'pipe:[49581132]'
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16
$ ls -l /proc/6944/fd
lr-x------. 1 tim tim 64 Jul 24 19:59 0 -> 'pipe:[49581132]'
lrwx------. 1 tim tim 64 Jul 24 19:59 1 -> /dev/pts/16
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16

Also if you're lucky, the different commands in the pipeline will get consecutive PIDs which will make it a bit easier.

I don't actually have a script to do this, but I have proved the concept.

Tags:

Bash