de[v|b]log

ShellScript, Coffee, iOS/OSX Dev
Origin: Himajinworks.
About.

今日は日曜ということもありちょっと休みを取りたかったのでほんの少しだけ作業を行った。

やったこと

これまで移行作業をOS X (10.11)環境で、Homebrewを利用してインストールしているzsh (5.1.2)で行っていた。 自分が常時OSX環境だけ利用しているのであればいいのだが残念ながらそんな訳もなく、他の*nix環境でも利用しているため、ここでLinux環境で動作に問題がないかだけでも一応のため確かめておこうと思い、Linux環境としてXubuntu (Ubuntu 14.04 / Linux Kernel 3.16.0)で動作させてみた。

結果から言うと見事に動かなかった。なぜか調べてみると、そもそもパッケージマネージャで降ってきているzshのバージョンがとても古く(zsh 5.0.2 (x86_64-pc-linux-gnu))、このことに起因するものであった。 実際に現行バージョンであるzsh 5.2-dev-0 (x86_64-unknown-linux-gnu)を手でビルドして移行したコードを動かしてみたところ動いた。

動作と環境

  • Works correctly
    • zsh 5.1.2 installed through Homebrew on OSX 10.11 El Capitan
    • zsh 5.2.0-dev-0 built manually on Xubuntu (Ubuntu 14.04 / Linux Kernel 3.16.0)
  • Not works
    • zsh 5.0.2 installed through apt on Xubuntu (Ubuntu 14.04 / Linux Kernel 3.16.0)

今日の内容

ということで今日はここの問題の理由についてと、アップデートがあった点を探す。

問題の特定

まず動作させてログを確認する。

1
him::tool::plugin_manager::base_task:11: bad pattern: list=(zsh-syntax-highlighting

ありがたい。とてもありがたい。一発で問題箇所がどこかわかった。 以下は上記と対応するスクリプト内の一行である。

1
local list=(${(s:,:)"${n_line}"})

ここでは文字列としてCSVファイルから一行読み込み、それをカンマをデリミタとして配列を生成している。 そのため、まずは部分的にこの問題を再現するコードを書いた。

1
2
3
4
5
6
7
8
9
10
#! /usr/bin/env zsh

echo "Version:"
/usr/bin/env zsh --version

line='foo,bar,baz'
list=(${(s:,:)"${line}"})

echo ${list}
echo ${list[1]}

が、しかしどうだろう。動作させてみると

1
2
3
4
Version:
zsh 5.0.2 (x86_64-pc-linux-gnu)
foo bar baz
foo

と、問題が再現できなかった。では何が問題か、更に同じような環境にしてみる。 今度は関数に処理を閉じ込め動作させてみる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#! /usr/bin/env zsh

function test_task()
{
    line='foo,bar,baz'
    list=(${(s:,:)"${line}"})

    echo ${list}
    echo ${list[1]}
}

echo "Version:"
/usr/bin/env zsh --version
test_task

が、しかしこれに関しても、

1
2
3
4
Version:
zsh 5.0.2 (x86_64-pc-linux-gnu)
foo bar baz
foo

と正常に動作した。では何が問題か、今度は関数内で使用している変数linelistlocal変数として宣言する。

1
2
3
4
...
    local line='foo,bar,baz'
    local list=(${(s:,:)"${line}"})
...

するとどうだろう、

1
2
3
Version:
zsh 5.0.2 (x86_64-pc-linux-gnu)
test_task:3: bad pattern: list=(foo

同様のエラーが得られた。 もしやlocal宣言と初期化が問題なのではと思い、今度は分割してみる。

1
2
3
4
5
...
    local line list
    line='foo,bar,baz'
    list=(${(s:,:)"${line}"})
...

すると、

1
2
3
4
Version:
zsh 5.0.2 (x86_64-pc-linux-gnu)
foo bar baz
foo

と、求めていた出力が得られた。 ここで、変数lineに関してはどのケースにおいても初期化時に問題が起きていなかったため、配列として扱う変数の初期化に関する問題であることがわかる。

更新があった部分を探る

ここまででzsh 5.0.2から5.1.2までの間に何らかの更新があったと察しがつくので、ZSH - Release Notesをいつもどおり確認する。すると、

Changes between versions 5.0.8 and 5.1 The builtins declare, export, local, readonly and typeset now have corresponding reserved words. When used in this form, the builtin syntax is extended so that assignments following the reserved word are treated similarly to assignments that appear at the start of the command line. For example, local scalar=echo one word array=(several words) creates a local "scalar" containing the text "one word" and an array "array" containing the words "several" "words".

ということで、しっかりと自分が欲しい情報があった。 正直ここまで大きく書いてあって気づかなかったのは情けない。

さて、上記の引用曰く、

  • 5.0.85.1.0にかけて、この問題に関する更新があった。
    • この更新でlocal等のbuiltin関数に対しての初期化時の拡張があった。

ということがわかった。

対策

ここで現在起きている問題をどう対処しようか考えた。現時点では以下の2つの選択肢がある。

  1. localによるローカル変数宣言と初期化を分割する。
  2. zsh (< 5.1.0)を使わない。

ここでどうしようか少し悩んだが、大体の身の回りの環境(ただし自分が利用している、問題の起きたLinux環境を除く)にはだいたいzsh (>= 5.1.0)がインストールされていたため、ここでは後者を選択した。 ただこれは個人の設定ファイルに対してのみであり、Githubで公開しているいくつかのプラグイン等に関しては1として挙げた対処を行っておきたい。

TODO

  • Githubで公開しているzshプラグイン群について、localによるローカル変数宣言と初期化を分割する。