paxdiablo
paxdiablo

Reputation: 882186

How can you override function redirection in bash?

I recently discovered some bash code that used the little-known (well, little known to me anyway) feature of function redirection, such as the greatly simplified:

function xyzzy () {
    echo hello
} >/dev/null

When you call the function with a simple xyzzy, it automatically applies the redirections attached to the function regardless of what you've done when calling it.

What I'd like to know is if there's any way to override this behaviour in the call to the function itself, to see the message being generated. I'm reticent to change the file containing all the functions since (1) it's large, (2) it changes regularly, and (3) it's heavily protected by the group that supports it.

I've tried:

xyzzy >&1

to try to override it but the output still doesn't show up (possibly because >&1 may be considered a no-op).


In other words, given the script:

function xyzzy () {
    echo hello
} >/tmp/junk

rm -f /tmp/junk
echo ================
echo Standard output
echo ----------------
xyzzy # something else here
echo ================
echo Function capture
echo ----------------
cat /tmp/junk
echo ================

it currently outputs:

================
Standard output
----------------
================
Function capture
----------------
hello
================

What can I change the xyzzy call to, so as to get hello printed in the standard output section rather than the function capture section?

And this needs to be without trying to read the file /tmp/junk after it's created since the actual redirections may be to /dev/null so they won't be in a file.

Upvotes: 9

Views: 2185

Answers (2)

glenn jackman
glenn jackman

Reputation: 247042

The only thing I can think of would be to parse the output of declare -f function_name and remove the redirection.

This is perhaps the easiest approach. Note that you need to tailor the awk script to the specific function layout and it doesn't modify the body of the function at all. That means you can only turn off redirection at the top level. You could modify whole call trees of functions to turn off redirection but that would require a bash parser capable of recognising and changing function calls within the body.

The following script shows how to do it with your sample function. All the awk command does is create a new function my_xyzzy which mirrors the xyzzy function except for the final line, effectively turning it into:

function my_xyzzy () {
    echo hello
}

And the complete script as per the specifications:

function xyzzy () {
    echo hello
} >/tmp/qqqq

declare -f xyzzy | awk '
    NR==1 {print "my_xyzzy ()"}
    NR==2 {prev=$0}
    NR>2  {print prev;prev=$0}
    END   {print "}"}' >$$.bash
. $$.bash
rm -f $$.bash

rm -f /tmp/qqqq
echo ================
echo Standard output
echo ----------------
my_xyzzy
echo ================
echo Function capture
echo ----------------
cat /tmp/qqqq
echo ================

The output of that is:

================
Standard output
----------------
hello
================
Function capture
----------------
cat: /tmp/qqqq: No such file or directory
================

Upvotes: 2

kron
kron

Reputation: 1

I don't think Bash function redirections can be overridden dynamically in the call to the function itself although a temporarily altered shell context can be made use of by combining Bash aliases and functions (see Magic Aliases: A Layering Loophole in the Bourne Shell).

Non-dynamically it is the last redirection expression, i. e. the rightmost one, that overrides the previous ones if the redirection expressions refer to the same file descriptor.

# example
ls -ld / no_such_file 1>/dev/null 1>/dev/tty 1>&2 1>redirtest.txt
cat redirtest.txt

Therefore, glenn jackman's suggestion to use declare -f function_name seems the way to add a final stdout redirection expression to override the previous ones.

xyzzy() { echo 'Hello, world!'; } 1>/dev/null
#func="$(declare -f xyzzy) 1>&2"
func="$(declare -f xyzzy) 1>/dev/tty"
eval "$func"
xyzzy

Upvotes: 0

Related Questions