On Bash-Hackers.org there is a nice little wiki entry on collapsing functions. Basically, a collapsing function is a function that redefines itself based on some condition. The basic example was something like the following:
chatter() {
if [[ $verbose ]]; then
chatter() {
echo "$@"
}
chatter "$@"
else
chatter() {
:
}
fi
}
I thought that was a nice little trick that might be useful for creating functions like the following:
# a portable extended regular expression sed for Linux and Mac
# simply checks if using an option fails for one version
# @see http://wiki.bash-hackers.org/howto/collapsing_functions
my_sed() {
if ( echo abc | sed -r /abd/p > /dev/null 2>/dev/null ) ; then
#we are running a GNU version. redefine function
echo gnu
my_sed() {
sed -r "$@"
}
else
#we are running another version. defaulting to BSD
#redefining function
echo osx
my_sed() {
sed -E "$@"
}
fi
my_sed "$@"
}
(The echo statements are just for debugging btw). This works as intended when running it as my_sed "s/foo/bar" /tmp/somefile.txt, outputting “gnu” (on Linux, osx on Mac) the first time, then keeping silent for subsequent runs. But if I simply use the function in pipes, the function is not being redefined, constantly outputting “gnu”. Example: echo 123 | my_sed 's/foo/bar' will output “gnu” every time it is being run in the shell.
Why is this? What is piping doing with the current context/shell that keeps the function from keeping its new definition? Is piping forking a new process each time, so that the new definition is lost from the originating shell?
pipelines are run in a subshell, and the redefinition is lost when the subshell exits.