2

I have a script in ksh; depending whether there is a redirection from the command line, I redirect the output via a exec 1>file. How can I test from inside the script itself if the command calling it has redirected its output?

I tried using $@, $*, $0, and even a ps on the PID of the script (hopefully there is a shebang) but the redirection never appears.

The script is running on AIX in this case.

ilkkachu
  • 138,973
  • 1
    Redirections never appear as any of the argument to any command as they are acted on by the calling shell before the command is executed. This sounds a lot like an XY-problem. What is it you'd like to achieve? – Kusalananda Jan 29 '19 at 10:23
  • 1st lien of the question. Script will redirect or not it's own content depending of a redirection call or not. I try to know from the script how the call was made from the parent. Reirection check is the way i could know how to manage it but maybe the good point of view. I don't know wo and how the script will be used so can't set info before – NeronLeVelu Jan 29 '19 at 10:40
  • Seems like documentation of the script and how it's supposed to be used might be in order. If a user knows to redirect its output, always, then document that and be done with it. Don't try to babysit the user any more than that. – Kusalananda Jan 29 '19 at 19:49

3 Answers3

3

On AIX, the stdout file descriptor is available at /proc/$$/fd/1, so you could test it for being a regular file or not:

if [ -f "/proc/$$/fd/1" ]
then
  echo stdout has already been redirected
else
  echo redirecting stdout
  exec 1>file
  echo some output
fi

/bin/sh is hardlinked to /bin/ksh, so you get the same behavior in either shell.

You could test separately for stdout having been redirected to /dev/null, if you wanted:

if [ "/proc/$$/fd/1" -ef /dev/null ]; then : ...; fi
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
2

In general, you can't. Redirections don't appear as arguments to the running command. Even if they did, you wouldn't be able to tell where the script output goes in all cases. Consider these two:

bash -c 'somecmd > /dev/null; othercmd'

and

bash -c 'somecmd; othercmd'  > /dev/null

In the first case, the output of somecmd is redirected to /dev/null, but in the second case, the output of the whole shell is redirected, including both somecmd and othercmd. Seeing the command line of somecmd in the second case wouldn't tell how the output is redirected.

That said, it appears Bash's DEBUG trap can be used for this.

$ trap 'export CMDLINE=$BASH_COMMAND' DEBUG
$ env 2>/dev/null |grep CMD
CMDLINE=env 2> /dev/null

The trap exports the command to be run as CMDLINE, which we can see is exported since it shows in the output of env. Note that the full pipeline is not shown, just the one command.


That said, in most cases there are better ways to deal with things than trying to second-guess the user's redirections. Many commands check if the output goes to a terminal and change their behavior based on that.

To check if stdout is a terminal, you can use [ -t 1 ]:

$ if [ -t 1 ]; then echo terminal; else echo not terminal; fi  |cat
not terminal

This is most often used to disable some interactive functionality or extraneous output in case the output doesn't go to a terminal and hence, by assumption, to a user.


If just testing if a file descriptor points to a terminal isn't enough, it might be easiest to arrange to pass an additional argument to the program to tell it what mode to operate in. That is, instead of caring about redirections, have the program do one thing if started with someprog --mode=cron, another if started with someprog --mode=batch and run interactively if started without a --mode argument. (Make interactive or command line mode the default so that the user doesn't need to manually type --mode=commandline each time they run it by hand.)

ilkkachu
  • 138,973
  • the test of terminal is not elligible because it is run from 1) comand line, 2) cron 3) external agent running a subshell (agent are not terminal session). This is the tricky point. I haven't thought about DEBUG, good point – NeronLeVelu Jan 29 '19 at 12:43
  • bad luck, cannot have this info without previously trap, that is not possible ... – NeronLeVelu Jan 29 '19 at 13:23
  • 1
    @NeronLeVelu, well, I didn't know (and still really don't know) what the actual use case was, so I mentioned testing for terminals since it's one common case. And yes, the DEBUG trap only works if set in the calling shell, and it might not be easy to use within, say, cron. (Though you could of course manually set the environment variable, but then you might just directly pass whatever information it is you're eventually interpreting from the redirections. Edited.) – ilkkachu Jan 29 '19 at 13:27
  • that's exactly my problem. i check via a lsof but assuming the PID of the script and using a inode reference. Assuming lot of interpretation is not the most reliable method – NeronLeVelu Jan 29 '19 at 14:42
1

In my testing, on Linux (Debian 10.12) with Bash 5 (GNU bash, version 5.0.3(1)-release (x86_64-pc-linux-gnu)), you can use the [ -p test.

  • Consider a script test.sh
    #!/bin/bash
    ls -lah /proc/$$/fd/1
    if [ -p "/proc/$$/fd/1" ]; then echo "pipe"; else echo "nopipe"; fi
    exec 1> >( cat ) 2>&1
    ls -lah /proc/$$/fd/1
    if [ -p "/proc/$$/fd/1" ]; then echo "pipe"; else echo "nopipe"; fi
    
  • Execute the script without and with redirection:
    $ ./test.sh
    lrwx------ 1 [...] /proc/123/fd/1 -> /dev/pts/2
    nopipe
    l-wx------ 1 [...] /proc/123/fd/1 -> pipe:[12345]
    pipe
    

    $ ./test.sh | cat l-wx------ 1 [...] /proc/124/fd/1 -> pipe:[12346] pipe l-wx------ 1 [...] /proc/124/fd/1 -> pipe:[12347] pipe

AdminBee
  • 22,803