3

I want to implement a script foo.sh that culminates in the an execution of a command bar (with some arguments). The command bar modifies the current shell environment, which means that foo.sh must be sourced1. (BTW, the implementation of bar is entirely outside my control.)

The purpose of most of the code in foo.sh is to compute the arguments that will be passed to bar.

In order to keep foo.sh reasonably legible/maintainable, I find that I must define many auxiliary variables. Unfortunately, this sets the stage for

  1. namespace collisions (i.e. clobbering existing parameters)
  2. namespace pollution (i.e. cluttering the environment with superfluous parameters)

One possible way to avoid, or at least mitigate, these problems is to put most of foo.sh's code into a function (with all its variables declared as local) that echoes a suitable string of code to be eval'ed within the calling scope; e.g.

{
  __messy_calculation () {

      local x y z ...
      local bar_arg_1 bar_arg_2 ...
      ...
      echo "bar $bar_arg_1 bar_arg_2 ..."

  }

  eval "$( __messy_calculation )"

} always {

  unfunction __messy_calculation

}

This takes care of the namespace pollution problem, and reduces the namespace collision problem to the one for the name of the function. (I'm not a big fan of using eval, though.)

I figure that this situation is generic enough that there already exist standard ways to address it. If so, please let me know.


1In case my reasoning here is flawed, let me just add that if I put all the code, including the call to bar, into a function, when I execute this function on the command line the current environment remains unaffected, which renders the function useless. In contrast, if I source the line that executes the call to bar, the current environment is modified as expected.

  • 2
    What makes you think you need to use eval? Just call the function. – psusi Nov 12 '15 at 0:21
  • 1
    You don't even need to define __messy_calcuation; zsh supports anonymous functions: function { local x y z; local bar_arg_1 ...; ...; bar $bar_arg_1 $bar_arg_2; } – chepner Nov 12 '15 at 14:27
0

For variables it is possible to use an anonymous function (as noted in the comment above). That means you can can define as many variables as you want. The ones you want to hide from the caller you prefix with local, the ones you want to communicate to the caller you just define normally:

function {
  local my_var=foo
  your_var=bar
  : ...
}

But for functions that will not work as they can not be declared local. I had this problem in my zshrc and decided to prefix all names of my "local" functions with a short string (like you do namespaces in C) and to do a wildcard unfunction at the end:

function foo-do-something () {
  : ...
}
function foo-do-some-more-stuff () {
  : ...
}
function foo-main () {
  local my_var=oof
  you_var=rab
  foo-do-something for bar
  foo-do-some-more-stuff with baz
}
foo-main
unfunction -m 'foo-*'

This is not an optimal solution in my opinion (I would prefer local functions) but it works for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.