Humanity

Edit the world by your favorite way

Go 言語の range は2番目の返り値に同じ参照を返す場合がある

言葉で説明するのだるいので次のコードを見てください。

package main

import (
    "fmt"
)

func main() {
    array := []int{1, 2, 3}
 
    // これだと &a が同じアドレスになってしまうことがある
    // (range は array[i] のコピーを返す)
    for i, a := range array {
        fmt.Printf("%d: %p\n", i, &a)
    }
    // こうするか
    fmt.Println("----------------------------")
    for i := range array {
        a := array[i]
        fmt.Printf("%d: %p\n", i, &a)
    }
    // こうしないといけない
    fmt.Println("----------------------------")
    for i := range array {
        fmt.Printf("%d: %p\n", i, &array[i])
    }
}

for i, a := range ... { とした時に &a が毎回同じアドレスになってしまうことがある、ということです(結局言葉で説明…)。 あくまで range の2番目の返り値は配列の各要素へのポインタではなく値を取得したい場合に使うらしいです。 なので以下のコードはダメ。

package main

import (
  "fmt"
)

type MyObj struct {
  id int
}

func main() {
  array := []MyObj{MyObj{id: 1}, MyObj{id: 2}, MyObj{id: 3}}

  // id >= 2 の要素だけ抜き出す
  filtered := filterGreaterThan1(array)
  // 出力すると自分の環境だと 3 が2回表示された
  for _, a := range filtered {
    fmt.Println(a.id)
  }
}

func filterGreaterThan1(array []MyObj) []*MyObj {
  filtered := make([]*MyObj, 0, len(array))
  for _, a := range array {
    if a.id > 1 {
      // a は同じアドレスの可能性があるため &a を持ちまわると同じ参照を指してしまうことがある
      filtered = append(filtered, &a)
    }
  }
  return filtered
}

Special Thanks

Subsonic に不満があったので OSS 版の Libresonic をインストールしてみた

追記

続編です。 これと組み合わせれば Subsonic でも曲をフォルダに置いたら即スキャンしてくれるようになります。

tyru.hatenablog.com

本文

ずっと Subsonic というストリーミングサーバーを使っていた。 1ドル /月 払えば Android アプリでも接続できるようになる。 概ね満足だったんだけどそれでもいくつか不満があった。

  1. flac ファイル特有の現象なのかは分からないが)flac ファイルを再生すると曲が終わっても次の曲に行ってくれない
    • 曲の時間が明らかに長かったりするので、終わりが正しく取得できてない?
  2. 音楽フォルダのスキャンが一日一回に限られている
    • 曲が追加されたら一定時間後に自動的にスキャンしてほしい
  3. 最近のバージョンのコードはクローズド(version 6.0-beta1 以降)

これらの問題点を解決するためにLibresonic という SubsonicOSS 版を試してみた。

ちなみに 2 に関しては解決しなかったが、スキャンを行う REST API はあるようなので、曲が追加されたら REST API 叩くスクリプトを書けば解決しそう。 *1 (追記 2017/8/27 0:30)勘違いでした。Subsonic にはありますが現在の Libresonic にはありません

Java 8+ のインストール

$ sudo yum install java-1.8.0-openjdk
$ java -version
openjdk version "1.8.0_141"
OpenJDK Runtime Environment (build 1.8.0_141-b16)
OpenJDK 64-Bit Server VM (build 25.141-b16, mixed mode)

java-1.8.0-openjdk を入れれば自動的に Java 8 になってるはず。 なってなかったら alternatives コマンドで Java 8 を選ぶようにしてください。 以下は Java 7 と 8 が入ってる環境の図。

$ sudo alternatives --config java

2 プログラムがあり 'java' を提供します。

  選択       コマンド
-----------------------------------------------
   1           java-1.7.0-openjdk.x86_64 (/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.141-2.6.10.1.el7_3.x86_64/jre/bin/java)
*+ 2           java-1.8.0-openjdk.x86_64 (/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.141-1.b16.el7_3.x86_64/jre/bin/java)

Enter を押して現在の選択 [+] を保持するか、選択番号を入力します:

Standalone バージョンのインストール

WAR を持ってきて java -jar {war} すれば動くやつです。 それを systemd で動かすようにします。

まずユーザの作成。

$ sudo useradd --system libresonic
$ id libresonic
uid=994(libresonic) gid=992(libresonic) groups=992(libresonic)

次に必要なディレクトリを作りつつ WAR をダウンロードしてインストール。

$ sudo mkdir /opt/libresonic
$ curl -LO https://libresonic.org/release/libresonic-v6.2.war
$ sudo install -o root -g root -m 644 libresonic-v6.2.war /opt/libresonic/libresonic.war

$ sudo mkdir /var/libresonic/
$ sudo chown -R libresonic:libresonic /var/libresonic/

次に systemd の設定ファイルを作成。

$ vim /etc/default/libresonic
JAVA_HOME=/usr/lib/jvm/jre

$ vim /etc/systemd/system/libresonic.service
[Unit]
Description=Libresonic
After=network.target

[Service]
Type=simple
EnvironmentFile=-/etc/default/libresonic
ExecStart=/usr/bin/java -jar /opt/libresonic/libresonic.war
User=libresonic
Group=libresonic
PrivateTmp=true
Restart=always

[Install]
WantedBy=multi-user.target

設定ファイルをロード。

$ sudo systemctl daemon-reload

ffmpeg のインストール

qiita.com

インストールした後、以下の手順を実行

$ sudo mkdir /var/libresonic/transcode
$ cd /var/libresonic/transcode
$ ln -s /usr/bin/ffmpeg
$ ls -alh
lrwxrwxrwx 1 user user   15 mai    4 19:57 ffmpeg -> /usr/bin/ffmpeg

起動

いよいよ起動ですが、結構時間かかります。 HP ProLiant MicroServer (Gen8 ではない) で 74秒 ぐらい。

都度 sudo systemctl status -l libresonic するのもだるいので、 sudo systemctl start libresonic したら journalctl -ef -u libresonic で起動するまでログを眺めておくといいでしょう。

$ sudo systemctl start libresonic
$ journalctl -ef -u libresonic

初回は必要なファイル作成や DB の初期化などでだばーっとログが出ます。 起動したらこんなログが出ます。

2017-08-21 00:43:55.960  INFO --- o.l.player.boot.Application              : Started Application in 72.08 seconds (JVM running for 74.073)

起動後 /var/libresonic がこんな感じになってました。

$ ls -F /var/libresonic/
db/  libresonic.log  libresonic.properties  lucene2/  rollback.sql

Could not verify the provided CSRF token because your session was not found.

公式のインストール手順通りにインストールして java -jar libresonic.war と実行してみたのだが、Web UI でログイン後にこんなページに飛ばされてしまう。

f:id:tyru:20170821011158p:plain

ログファイル(/var/libresonic/libresonic.log)を見ても特にエラーは出ていなかった。

Tomcat のインストールしたら直った?

$ sudo yum install tomcat
$ sudo yum remove tomcat tomcat-lib

とかやってたら正常にログインできるようになってしまった。謎。

========================================================================================================================================================================================
 Package                                                 アーキテクチャー                    バージョン                                      リポジトリー                          容量
========================================================================================================================================================================================
インストール中:
 tomcat                                                  noarch                              7.0.69-12.el7_3                                 updates                               89 k
依存性関連でのインストールをします:
 apache-commons-collections                              noarch                              3.2.1-22.el7_2                                  base                                 509 k
 apache-commons-daemon                                   x86_64                              1.0.13-6.el7                                    base                                  54 k
 apache-commons-dbcp                                     noarch                              1.4-17.el7                                      base                                 167 k
 apache-commons-logging                                  noarch                              1.1.2-7.el7                                     base                                  78 k
 apache-commons-pool                                     noarch                              1.6-9.el7                                       base                                 113 k
 avalon-framework                                        noarch                              4.3-10.el7                                      base                                  88 k
 avalon-logkit                                           noarch                              2.1-14.el7                                      base                                  87 k
 ecj                                                     x86_64                              1:4.2.1-8.el7                                   base                                 1.4 M
 geronimo-jms                                            noarch                              1.1.1-19.el7                                    base                                  31 k
 geronimo-jta                                            noarch                              1.1.1-17.el7                                    base                                  20 k
 javamail                                                noarch                              1.4.6-8.el7                                     base                                 758 k
 log4j                                                   noarch                              1.2.17-15.el7                                   base                                 443 k
 tomcat-el-2.2-api                                       noarch                              7.0.69-12.el7_3                                 updates                               80 k
 tomcat-jsp-2.2-api                                      noarch                              7.0.69-12.el7_3                                 updates                               93 k
 tomcat-lib                                              noarch                              7.0.69-12.el7_3                                 updates                              3.8 M
 tomcat-servlet-3.0-api                                  noarch                              7.0.69-12.el7_3                                 updates                              211 k
 xalan-j2                                                noarch                              2.7.1-23.el7                                    base                                 1.9 M
 xerces-j2                                               noarch                              2.11.0-17.el7_0                                 base                                 1.1 M
 xml-commons-apis                                        noarch                              1.4.01-16.el7                                   base                                 227 k
 xml-commons-resolver                                    noarch                              1.2-15.el7                                      base                                 108 k

このうちのどれかが必要なパッケージだったのか?*2 今となっては謎。雑。

*1:でもどうせなら Libresonic 本体にそういう機能があってほしいのでいつか pull request 送りたい

*2:そもそも入るバージョンは Tomcat 7 だけど公式ドキュメントで説明されてるディレクトリ名が tomcat8 なので 8 のはず…(明確にどのバージョン使えとは書いてない…)

Vim の :terminal がもたらすもの

追記

本記事を書いた後いろいろ状況が変わって :terminal コマンドが使える Vim が配布されるようになったので、 とりあえず現時点(2017/09/15)での状況を書きました。

tyru.hatenablog.com

序文

今絶賛開発中の Vim の :terminal コマンドについて書こうと思う。

:terminal コマンドを実行するとシェルが開き、Vim 上で操作することができる、というもの。 つまり実質的に Vim は端末ソフトウェアとなった。 その上 Vim script で端末上の様々な内容にアクセスしたり変更することができる。

expect コマンド、parallel-ssh のようなツールを Vim script で自作することも可能となった。 しかし、すでにそれらのコマンドがある以上 Vim プラグインで実現する必要性は薄いだろう。

Vim の :terminal がもたらすもの、それは外部コマンドとのさらなる相互運用性(interoperability)だと思う。

Vim 8 リリース時にも多数の機能が追加された。 job 機能や JSON パーサー/シリアライザーも他プロセスと通信するための大きな前進となった。 それが今度は :terminal によって端末まで内包してしまった。 つまり Vim ユーザは直接プロセスを起動する UI も手に入れた。*1

mattn さんが作った vim-fz というプラグインがある。 これは言ってみれば denite.vim(unite.vim)のような機能を外部コマンドにより実現したものだ。 これまで Vim script で実現するしかなかった部分を外部コマンドで代替することができるようになった。 vim-fz を見ると、これからは Vim プラグインの作り方も大きく変わってくる、そんな気がする。

Windows Subsystem Linux

最近 Vim 以外で嬉しいニュースもあった。 Windows Subsystem Linux(以下 WSL)がベータ版じゃなくなったことだ。

blogs.msdn.microsoft.com

もともと自分は Linux 環境を手に入れるために MSYS2 を使っていたが、WSL が正式にサポートされたことによりちょっとの手順でサクッと Linux 環境が手に入る。

しかし今度は端末ソフトがない。 なので MSYS2 で使っていた mintty の WSL 版である wsltty という端末ソフトを仕方なく使っていた。

tyru.hatenablog.com

そこにちょうどタイミング良く Vim が端末機能を実装した。

mattn.kaoriya.net

慣れ親しんだ Vim のキーストロークでコピペができる。 ウィンドウ分割ができる。 これがどんなに嬉しいことか…

ちなみに自分はこれまでシェルを複数起動したい場合、端末を複数起動していた。 同じように tmux や screen をただの多画面でシェルを起動するだけの使い方をしていた Vim ユーザはその必要もなくなった(セッション機能は Vim には無いが…)。

ある Windows ユーザーが Linux を求める旅が終わりを告げようとしている。

Vim を初めて見たのは Linux でのことだった。 その時はなんて質素なエディタだと思ったけど、:syntax on したら色がつくようになって、これでいいかと使い続けた。 それから約10年。 こんなに使い続けるとは思わなかった。

いつの間にかプラグインを書くようになってて、vim-jp ができてリポジトリGitHub に移って、Vim が随分と変わった。*2 これまで Vim のことが頭おかしくなるくらい好きだったけど、頭は無事おかしくなって今までよりももっともっと Vim のことが好きになってきた気がする。

:terminal コマンドを使える Vim を手に入れる

そんな Vim の :terminal コマンドはどうすれば使えるのかというと、 まだ現状配布されている Vim で使えるようになっているものはない(はず)。 Kaoriya 版も :terminal コマンドが使えるバージョンはまだ配布されていない。 Vim の nightly build でも --enable-terminal 付きでビルドされていないので使えなかった。

まだ現状どんどん仕様が変わっている最中のため仕方ないかもしれない。 けどベータ版であっても使ってみたい!と思う人はきっといっぱいいるはず。 WindowsVim をビルドする環境整えるのは結構大変だから、ベータ版を配布してもらえるとかなり嬉しいのですがどうでしょうかね…?(チラッ

Windows はそんな感じでビルドしんどいのでやってないけど、Linux や WSL 環境ではビルドできて(まぁ他の方も言ってる通り)Vim から普通に ssh したりできてるのを確認してます。 手順は以下の記事を参考にしてください。

vim-jp.org

私は毎回 configure にオプション付けたり更新したりめんどいので自作のビルドスクリプトを使ってビルドしてます。

Vim のビルドスクリプト - Humanity

そんな感じで、WSL が入ってて WSL 上で :terminal が使える Vim にパスが通っているなら、 ファイル名を指定して実行から bash --login -c "vim +terminal +only" でとりあえず WSL 上の Vim で端末を開始した状態で起動することはできる。 ただ MSYS2 だとそんなことないんだけど、WSL からはクリップボードレジスタが使えず、コピペするには端末(この場合 cmd.exe)のコピペ機能を使うことになる。 どうにかできるものかも分からないけどどうにかしたい。

バグ報告

ちなみに使ってみたけどなんかこの挙動おかしくない? って場合はここに投稿するといいです。 現状端末機能(だけじゃないけど)に関するパッチを書いてるメインコントリビュータの方々が見てる(というか書き込んでる)ので直してもらえるはず。

github.com

ちなみに

この記事を公開して日付見て初めて気付いたのですが、明日誕生日なのでなにかください(WindowsGVim +terminal でもいいです)。

https://www.amazon.co.jp/registry/wishlist/2DPWFJWB4GSNY/ref=cm_sw_r_tw_ws_x_XQ3dybZ1HC14Hwww.amazon.co.jp

*1:もちろんこれまでも :!cmd のように外部コマンドを実行することはできたが、対話プログラムは無理だった

*2:http://tyru.hatenablog.com/entry/20130326/vimmers_over_vim_jp