2022-01-23

Zsh: local `path' variable causes `command not found'

訳あって Zsh を使うことになり、手持ちの Bash 資産を Zsh に対応させていた時のこと。下記のようなコードでハマった。

test.sh:

my_dirname() {
    local path=$1
    dirname "$path"
}

my_dirname "$@"
% zsh test.sh /path/to/file
my_dirname:2: command not found: dirname

Zsh 使いの人にとって、きっとこれは FAQ なのだろうな。Zsh の path 変数は特殊変数なので、path を書き換えることは PATH を書き換えることに等しい。しかしこれは Zsh 初心者の私にとっては完全に盲点だった。そもそも小文字の変数が特殊変数になるという発想がない。お陰で解決に 3 週間かかった。:-/

他にどういう特殊変数があるかは、man zshparam | grep -F '<S>' すれば分かるっぽい。それにしても path とか status とか、これらの変数名が使えないのは私には窮屈でしょうがない。正直これだけでも、私が Zsh を使いたくない理由になる。

% man zshbuiltins

<...snip...>

       typeset [ {+|-}AHUaghlmrtux ] [ {+|-}EFLRZip [ n ] ]
               [ + ] [ name[=value] ... ]
       typeset -T [ {+|-}Uglrux ] [ {+|-}LRZp [ n ] ]
               [ + | SCALAR[=value] array[=(value ...)] [ sep ] ]
       typeset -f [ {+|-}TUkmtuz ] [ + ] [ name ... ]
              Set or display attributes and values for shell parameters.

              <...snip...>

              -h     Hide:  only  useful  for special parameters (those marked
                     `<S>' in the table in zshparam(1)), and for local parame‐
                     ters  with  the  same name as a special parameter, though
                     harmless for others.  A special parameter with  this  at‐
                     tribute  will not retain its special effect when made lo‐
                     cal.  Thus after `typeset -h PATH', a function containing
                     `typeset  PATH'  will  create an ordinary local parameter
                     without the usual behaviour of PATH.  Alternatively,  the
                     local parameter may itself be given this attribute; hence
                     inside a function `typeset -h PATH' creates  an  ordinary
                     local parameter and the special PATH parameter is not al‐
                     tered in any way.  It is also possible to create a  local
                     parameter  using  `typeset  +h  special', where the local
                     copy of special will retain its  special  properties  re‐
                     gardless  of having the -h attribute.  Global special pa‐
                     rameters loaded from shell modules  (currently  those  in
                     zsh/mapfile  and  zsh/parameter)  are automatically given
                     the -h attribute to avoid name clashes.

どうやら typeset -h を使うと、特殊変数をローカル変数で隠せるようだ。このときエラー出力を捨てれば、Bash にも対応できる。

test2.sh:

my_dirname() {
    typeset -h path 2>/dev/null
    local path=$1
    dirname "$path"
}

my_dirname "$@"
% zsh test2.sh /path/to/file
/path/to

もしスクリプト全体で path を特殊変数として使わないと決められるなら、typeset -h をグローバルに置けば良い。逆に Bash を全く気にしないのであれば、「local -h path=...」と 1 行で書ける。

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。