10

I'm trying to contain the scope of a variable to a shell, and not have children see it, in zsh. For example, I type this in .zshrc:

GREP_OPTIONS=--color=always

But if I run a shell script with the following:

#!/bin/bash
echo $GREP_OPTIONS

The output is:

--color=always

while I want it to be null (the shell script above should not see the GREP_OPTIONS variable at all).

In bash, one can say: export -n GREP_OPTIONS=--color=always, which will prevent this from happening. How do I accomplish this in zsh?

  • 1
    export -n just unexports an exported variable. – terdon Jan 27 '14 at 23:08
11

export in zsh is shorthand for typeset -gx, where the attribute g means “global” (as opposed to local to a function) and the attribute x means “exported” (i.e. in the environment). Thus:

typeset +x GREP_OPTIONS

This also works in ksh and bash.

If you never export GREP_OPTIONS in the first place, you don't need to unexport it.

You can also use the indirect, portable way: unsetting a variable unexports it. In ksh/bash/zsh, this doesn't work if the variable is read-only.

tmp=$GREP_OPTIONS
unset GREP_OPTIONS
GREP_OPTIONS=$tmp
  • See also env -u GREP_OPTIONS your-script with some env implementations (any shell). Or (unset GREP_OPTIONS; exec your-script) – Stéphane Chazelas Jan 28 '14 at 22:30
  • I tried typeset +x, but that doesn't make a difference either. As shown in my question, something is exporting all variables I define, even if I don't include "export". Still trying to find out why. – PonyEars Feb 5 '14 at 5:13
  • @redstreet Maybe you accidentally set the export_all (-a) option? But even then typeset +x GREP_OPTIONS would unexport the variable. If you can't find what's wrong, try binary search: back up your .zshrc, remove the second half, see if the problem still arises, then either append the third quarter or cut down to the first quarter and repeat. – Gilles Feb 5 '14 at 9:45
  • @Gilles Thanks, I found it: zsh has an "allexport" option that I had in my .zshrc. 'setopt noallexport' can turn it off temporarily if it helps anybody else. I'll accept your answer since it came the closest. – PonyEars Feb 5 '14 at 10:10
  • Now I have a different problem, which is closer to the problem I'm trying to solve, here: unix.stackexchange.com/questions/113645/… – PonyEars Feb 5 '14 at 10:15
6

You could use an anonymous function to provide a scope for the variable. From man zshall:

ANONYMOUS FUNCTIONS
       If no name is given for a function, it is `anonymous'  and  is  handled
       specially.  Either form of function definition may be used: a `()' with
       no preceding name, or a `function' with an immediately  following  open
       brace.  The function is executed immediately at the point of definition
       and is not stored  for  future  use.   The  function  name  is  set  to
       `(anon)'.

       Arguments to the function may be specified as words following the clos‐
       ing brace defining the function, hence if there are none  no  arguments
       (other than $0) are set.  This is a difference from the way other func‐
       tions are parsed: normal function definitions may be followed  by  cer‐
       tain  keywords  such  as `else' or `fi', which will be treated as argu‐
       ments to anonymous functions, so that a newline or semicolon is  needed
       to force keyword interpretation.

       Note also that the argument list of any enclosing script or function is
       hidden (as would be the case for any  other  function  called  at  this
       point).

       Redirections  may be applied to the anonymous function in the same man‐
       ner as to a current-shell structure enclosed in braces.  The  main  use
       of anonymous functions is to provide a scope for local variables.  This
       is particularly convenient in start-up files as these  do  not  provide
       their own local variable scope.

       For example,

              variable=outside
              function {
                local variable=inside
                print "I am $variable with arguments $*"
              } this and that
              print "I am $variable"

       outputs the following:

              I am inside with arguments this and that
              I am outside

       Note  that  function definitions with arguments that expand to nothing,
       for example `name=; function $name { ... }', are not treated as  anony‐
       mous  functions.   Instead, they are treated as normal function defini‐
       tions where the definition is silently discarded.

But apart from that - if you're not using export in your .zshrc at all, the variable should only be visible in your current interactive session, and it shouldn't be exported to subshells.

As terdon explained in his comment: export -n in bash just causes the "export" property to be removed from the variable, so using export -n GREP_OPTIONS=--color=always is equivalent to not using export at all - GREP_OPTIONS=--color=always.

In other words, to get the desired behavior, just don't use export. Instead, in your .zshrc, you should have

GREP_OPTIONS=--color=always

That will make the variable available to all (interactive, non-login) shells you run, just as you want it to be, but it won't be exported to child shells.

  • Just having GREP_OPTIONS=--color=always" is what I'm doing, as shown in my question. Something else in zsh is causing all my variables to be exported automatically. Still trying to find out what that is. – PonyEars Feb 5 '14 at 5:17

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.