zsh: typeset -Ug PATH

自分の zshrc 内では、$PATH の重複を排除するために typeset -U PATH と書いていた。

普段はこれで上手く行くが、関数内で source ~/.zshrc すると zshrc 内でエラーが沢山出た。

$ cat ~/.zshrc
function reload_zshrc {
  source ~/.zshrc

$ reload_zshrc
zsh:17: command not found: find
zsh:17: command not found: find

source 内では command not found となっているが、 reload_zshrc した後も $PATH は変わらず、コマンドも使えていた。 また、関数を経由せずに source ~/.zshrc とした時は正常に動いていた。


これは、typeset は関数内の場合、 ローカル変数を作成 し、関数が終わったら unset するという挙動によるものである。

          Except  as  noted  below for control flags that change the behavior, a parameter is
          created for each name that does not already refer to one.  When inside a  function,
          a  new  parameter is created for every name (even those that already exist), and is
          unset again when the function completes.  See `Local  Parameters'  in  zshparam(1).
          The  same  rules  apply  to  special  shell  parameters, which retain their special
          attributes when made local.

Ubuntu Manpage: zshbuiltins - zsh built-in commands

なので、 reload_zshrc の中で typset -U PATH することで PATH の内容が初期化されてしまい、 command not found となっていた。

echo $PATH # /usr/local/bin:/bin: ...
typeset -U PATH
echo $PATH # (Empty output)


これを防ぐためには、 typset -g で global scope の PATH を使用するようにする。

          -g     The -g (global) means that any resulting parameter will not be restricted to
                 local  scope.   Note  that this does not necessarily mean that the parameter
                 will be global, as the flag will apply to any existing  parameter  (even  if
                 unset)  from an enclosing function.  This flag does not affect the parameter
                 after creation, hence it has no effect when listing existing parameters, nor
                 does the flag +g have any effect except in combination with -m (see below).

Ubuntu Manpage: zshbuiltins - zsh built-in commands

echo $PATH # /usr/local/bin:/bin: ...
typeset -gU PATH
echo $PATH # /usr/local/bin:/bin: ...