in Antarctica

いろんなメモ

fishの文法を書くのがめんどくさい人のためのTips

fish shellを使って2年がたちました

今では結構使っている人も多いのではないでしょうか

fishはとても使いやすい良いshellです. ただbashzshと文法が大きくことなります.

特に苦労する機会が多いのは環境変数の文法や &&, || が使えないことだと思います.

僕の2年悩んだ苦渋にまみれた解決策があるのですが、 余り同じようなことをしている人をみないので共有します.

苦渋にまみれているので、デメリットもあることを念頭に置いておいてください.

fishの環境変数

bashzshでは環境変数

export HOGE=hensu-dayo

と書くと思います.

fishでは

set -x HOGE hensu-dayo

こうですね.

全然違うので web上に乗っているscriptを毎回書き直すの 面倒です.

苦渋にまみれた解決策

  1. まずデフォルトshellをbashに変更します.

  2. そして以下のように.bash_profileに環境変数を記述し fishを起動します 😇

.bash_profile

export HOGE=hensu-dayo

exec fish

これであら不思議, 環境変数bashのままfishを使えます 😇

デメリットとして, bashを起動しようとしてもfishが開きます 😇

&& ||が使えない

bashでは

echo hoge && echo foo

というように記述しますが,

fishでは

echo hoge; and echo foo

といった風になります.

書き直すのめんどくさいですね

苦渋にまみれた解決策

  • bash -c オプションを使う
bash -c 'echo hoge && echo foo'

これでそのままコピペできますね😇

それでもやっぱりめんどくさい😇

fishの関数を作って少しでも楽をします. 以下のようなfishスクリプト~/.config/fish/functions/bash_c.fishとして保存します.

~/.config/fish/functions/bash_c.fish

function bash_c
  read --prompt="echo 'write bash script: '" bash_script
  bash -c $bash_script
end

これでfishでbash_cをうち、bashスクリプトを入力するとbashを実行できます.

ただしあくまでbashのプロセスが立ち上がり実行されるので、ここで環境変数の設定とかはできません😇

だれか良い方法を教えてください😇

DockerでGo × mysqlの最低限な開発環境を用意する

今更ながらDockerの勉強をしています。 色々わからないところが多く、詰まったりしたところが多かったので自分はこのようにしたというのをまとめて見ます。output大事かなって思ったので

他にやり方があるよとかあれば、教えていただけると嬉しいです。

一応Dockerとか関係なく、 ローカルでさえ設定ができませえんって人が読めるくらいのレベルで書きたいです。

コードは全てgithubにおいておきます。 記事では関係ないところは飛ばしていたりします。 疑問があればご確認ください. この記事のコード

この記事の目標

コマンドを一つ叩くと、 (データがなければ)初期化されたmysqlコンテナ、goのwebコンテナが立ち上がり、 今回はmysqlに入ったデータを取得し、jsonで返すくらいにしておきます。

この際、もちろんですが、データは永続化されていて、 ついでにmysqlのログがローカルで見れるようにします。

docker-composeが動く環境にだけしておいてください。

下準備:mysqlコンテナ

mysqlコンテナの下準備をします。 具体的には,

  • 文字コードの変更
  • logをローカルで見れるようにする
  • 初期データを作る

文字コードの変更

公式のmysql imageを使いますが、 そのままだと日本語を入れると文字化けしてしまいます.

解決策は調べてみた感じで複数あるようです。

  • mysqlの設定ファイルをVOLUMEを使って書き換える(これを選択)
  • mysqlの公式のコマンドを使う(試してない)

mysqlの設定ファイルをVOLUMEを使って書き換える

僕はこれを使いました。 query logを見たくなった時とかにどうせ書き換える必要があるし

という訳で設定ファイルを用意します. mysql系の設定ファイルはmysqlというでディレクトリを作ってそこに入れることにします。

./mysql/my.cnf
...
[client]

...

default-character-set=utf8

...

[mysqld]

...

character-set-server=utf8

文字コードをutf8に変更しておきましょう。 寿司ビール問題とかはとりあえず無視でします。

mysqlの公式のコマンドを使う(試してない)

見にくくなるのこちらに書きました.

logをローカルで見れるようにする

ここでは、とりあえずerr-logをローカルで見れるようにします。

そもそもエラーログを出力させるために my.cnfを編集しましょう.

./mysql/my.cnf
[mysqld]

...

log-error      = /var/log/mysql/error.log

次にローカルにログを出力するためのディレクトリを作っておきます。

mkdir mysql/log

ログの下準備は以上です。

初期データを作る

やり方は色々あるでしょうが、 ここでは、mysqlの公式コンテナでは, コンテナ内の/docker-entrypoint-initdb.dというディレクトリにある .sqlファイルと.shファイルをコンテナ作成時に読み込んでくれるというものを利用します。

ローカルに初期データ用のディレクトリを作っておき, 以下のようなsqlファイルを作っておきます.

./mysql/init/0_init.sql
CREATE DATABASE test_db;

CREATE TABLE test_db.test_table(id int, name varchar(256));

INSERT INTO test_db.test_table VALUES(1, "ひょーが");

/docker-entrypoint-initdb.d以下のファイルはアルファベット順に読み込まれるようなので、 0_hoge.sql, 1_foo.shのようにしておくと楽かもしれません。

mysql下準備まとめ

ここでは以下の三つのことをするための下準備をしました。

  • 文字コードの変更
  • logをローカルで見れるようにする
  • 初期データを作る

ここまでの操作は全てローカルで行われています。 ディレクトリ構成は以下のようになりました。 詳しくはgithubで確認して見てください. この記事のコード

.
|--mysql
   |--init
   |  |--0_init.sql
   |--log
   |--my.cnf

下準備:Golangコンテナ

特になんてことはしません. ここでは以下のことをします.

  • jsonを返すだけのコードをかく
  • vendoringする

jsonを返すだけのコードをかく

./src/cmd/main.go
package main

import (
    "database/sql"
    "fmt"

    "github.com/gin-gonic/gin"
    _ "github.com/go-sql-driver/mysql"
)

type User struct {
    ID   int
    Name string
}

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        db, err := sql.Open("mysql", "root:password@tcp(db-server:3306)/test_db")
        defer db.Close()
        if err != nil {
            fmt.Println(err.Error())
        }

        rows, err := db.Query("SELECT * FROM test_table")
        defer rows.Close()
        if err != nil {
            fmt.Println(err)
        }

        user := User{}
        for rows.Next() {
            err = rows.Scan(&user.ID, &user.Name)
            if err != nil {
                fmt.Println(err)
            }
            fmt.Println(user)
        }

        c.JSON(200, gin.H{
            "hello": user.Name,
        })
    })
    r.Run()
}

コードが汚い?ハードコーディング? なんの事やら

vendoringする

これもやり方は複数あると思います。 何がベストプラクティスかわかりません、教えていただけると幸いです。

ここでは、depというものを使いました。

cd src
dep init

とするとsrc以下にvendorディレクトリが作成され、 依存パッケージが入ってくれます。 詳しい使い方は調べて見てください。

余談

このvendoringはgolangで用いている 依存パッケージをコンテナ内で使うために行なっています。

ここでは、依存パッケージをVOLUMEでコンテナ内に追加する事で解決しています。 ですが、コンテナを立ち上げ時に依存パッケージを全部ダウンロードということもできます。 他にも方法はあるでしょう。

何が良いのかぶっちゃけわからないので、教えてください。

Golang下準備まとめ

ここでは、以下のことをしました。

  • jsonを返すだけのコードをかく
  • vendoringする

ここまでのディレクトリ構成は以下のようになっています。

.
|--src
|  |--Gopkg.lock
|  |--Gopkg.toml
|  |--cmd
|  |  |--main.go
|  |--vendor
|     |--github.com
|        |--gin-contrib
|        |--.....
|        |--.....
|        |--.....
|
|--mysql
   |--init
   |  |--0_init.sql
   |--log
   |--my.cnf

docker-compose.ymlをかく

ここからが本番です。 実際にmysqlのコンテナとgoのコンテナを立ち上げ、 動かします。

先にdocker-compose.ymlが結局こうなったというのを示します。

./docker-compose.yml
version: '3.3'

services:
  db-server:
    image: mysql
    ports:
      - "3306:3306"
    volumes:
      - "data:/var/lib/mysql"
      - "data:/var/log/mysql"
      - "./mysql:/etc/mysql/conf.d"
      - "./mysql/init:/docker-entrypoint-initdb.d"
      - "./mysql/log:/var/log/mysql"
    environment:
      mysql_root_password: password
      
  app:
    image: golang:1.8-onbuild
    ports:
      - "8080:8080"
    volumes:
      - ".src/vendor:/go/src/github.com"
      - "./src:/go/src/app"
    depends_on:
      - db-server
    command: go run cmd/main.go

volumes:
  data:

data volume

Dockerではコンテナを起動し、停止すると基本的にコンテナ内のデータは消えます。 永続的なデータを扱いたい時はData用のVOLUMEを用意します。

volumes:
  data:

これだけで用意してくれるようです.

今回はmysqlのデータを永続化したいです。

services:
  db-server:
   volumes:
      - "data:/var/lib/mysql"
      - "data:/var/log/mysql"

このようにdb-serverのvolumesにmysqlのデータ保存先を追加します. 今回はlogも永続化しておきます。

mysqlの下準備を適用する

前半に書いたmysqlの下準備を適用していきます。 dockerのVolumeを用いて、コンテナ内の設定ファイルをローカルのものと置き換えます。

services:
  db-server:
   volumes:
      - "./mysql:/etc/mysql/conf.d" #設定ファイルを置き換える
      - "./mysql/init:/docker-entrypoint-initdb.d" #ローカルのinitとコンテナのdocker-entrypoint-initdb.dを繋げる
      - "./mysql/log:/var/log/mysql" #ローカルの./mysql/logとコンテナと/var/log/mysqlを繋げる
ローカルのパス:コンテナ内のパス

という風に書きます。

設定ファイルを置き換えたり、ローカルのディレクトリとコンテナのディレクトリをつなげたりできます。 これによってmy.cnfは置き換えられ、mysqlで出力されたlogはローカルの./mysql/logでも確認できるようになります。

あとは3306portを使用するので、あけておきます

goの下準備を適用する

mysqlと同じです。 volumeを使ってコードと依存パッケージをコンテナ内と共有します。

services:
  app:
    image: golang:1.8-onbuild
    volumes:
      - ".src/vendor:/go/src/github.com"
      - "./src:/go/src/app"

気をつけることはそんなにないですが、 goの公式imageでは GOPATHが/goになっているくらいでしょうか

特に意味はないですが、

docker-compose exec app bash

とかで、直接コンテナ内でビルドできたりするのでbuild済みのものを使いました. (一回もしませんでしたが)

あとはこちらも8080ポートを開けておきます

コンテナ同士の接続

今回は, GoコンテナからMySQLコンテナに接続する必要があります。 link(非推奨)というものを使うか、networkというものを使う方法があります. docker-composeのversion3を使うと、 services以下に定義したコンテナ同士はデフォルトで、 同じnetworkによしなに属してくれます。

名前は,

services:
  app:  #ここ
    ....
  db-server:  #ここ
    ....

のものが使われます。

なので、片方のコンテナから(例えば、今だとappのコンテナから)

ping db-server

するとちゃんとpingが通ります.

動かす

一思いに,

docker-compose up

します。

docker-compose.ymlにdepends_onと書いていると思うのですが、 起動する順番が決まるだけで、mysqlが起動するまで待ってくれないようです。

少し待ってから, http://localhost:8080/pingにアクセスすれば、 mysqlから取得したデータが入ったjsonが返ってきているはずです.

f:id:g-hyoga:20180129002332p:plain
結果

リガチャー(合字)が使えるエディターを適当に見てみた 

リガチャ使っていますか? これ f:id:g-hyoga:20170221231043p:plainf:id:g-hyoga:20170221231053p:plain こうなります。 カッコ良いね、コード書きたくなるね

リガチャ

複数の文字を一つの文字として、 表示してくれます。

最近話題になっている気がしますが、 以外と使えるエディターが少ない?気がしたので調べてみました。

美しい環境はやる気に繫がると思いますので、 ぜひ使ってみてください。

今回は都合で(めんどくさくて)macでよく使われている気がする エディタ(IDEも見ます)を調べてみました。

いろいろなリガチャーがありますが、 今回はFira Codeの

=> -> >= <= == :=
~= ~> <!-- -->

だけ見ていくことにします。

atom

f:id:g-hyoga:20170221231053p:plain さすがatomですね〜 綺麗に使えます!

注意

v1.0.0から正式に使えるようです。 updateしてなかったらupdateしてね

IntelliJ

f:id:g-hyoga:20170221231118p:plain

使えますね。 エディタというよりIDEですが、 気にしません

versionはver2016.3.4です

こちらの記事 見るととてもわかりやすいです。

Eclipse Neon.2

f:id:g-hyoga:20170221231131p:plain

使えますね〜 versionは4.6.2

iterm2

f:id:g-hyoga:20170221231144p:plain こんな感じ。 ちょっと変ですね、 というのも、 まだNightly Buildsでしか使えません。

まあそれでも使いたいvimmerなら使いましょう 僕はvimmerなので使います。

使えなかったもの

 - sublime  くらい

最後に

結局 4つくらいしか見てません。

というのも、 Fira Codeのgithubページ を見ると 対応しているものがだいたい書いてます。(書いてる途中に思い出した)

ですが、eclipseは使えないと書いてあったりするのと、 リガチャーの啓蒙のために投稿します。 (せっかく書いたし)

対応しているのが少ない気がしていたけれど、 なんだかんだで結構対応してましたね。

macの標準terminalなんかも対応してるよ〜 よかったらリガチャー使ってね

SICP

SICP勉強会してるので、 気が向いたら知見書いていけたらなぁと

exe2.63

(define (tree->list-1 tree)
    (if (null? tree)
        '()
        (append (tree->list-1 (left-branch tree))
                        (cons (entry tree)
                              (tree->list-1 (right-branch tree))))))

(define (tree->list-2 tree)
    (define (copy-to-list tree result-list)
        (if (null? tree)
            result-list
            (copy-to-list (left-branch tree)
                          (cons (entry tree) 
                                (copy-to-list (right-branch tree) result-list)))))
    (copy-to-list tree '()))

問題はtree->list1とtree->list2が同じリストを生成するのか、 オーダーはどうかである。 生成されるリストは順序ありリストという縛りがある。

前者は、生成されるリストは 一意に決定されるので、一つに定まる。

後者は、 tree->list1は

f:id:g-hyoga:20170207154923p:plain

こんな感じの順番で評価される

tree->list2は

f:id:g-hyoga:20170207155013p:plain

はこんな感じ.

これだけの違いしかなく、 それぞれ要素数nの場合 tree->list1はn回、 tree->list2(copy-to-list)もn回呼ばれる。

ここでtree->list1はappendを用いてlistの結合をしているので、 さらにO(n)かかる。 tree->list2はconsで済んでいる。

結果的にtree->list1は O(n2)、 tree->list2は O(n)

ハフマン木が面白かったから、 そこまで書こうと思ったけど疲れたから終わり

fishでVundle使うときの罠

shellをfishに変えて意気揚々とvimプラグインを追加して:BundleInstallすると次のようなエラーがでる

Error detected while processing function vundle#installer#new[24]..<SNR>59_process[11]..vundle#installer#run[8]..vundle#installer#install[12]..<SNR>59_sync[6]..<SNR>59_make_sync_command[4]..<SNR>59_get_current_origin_url[3]..<SNR>59_system:

まあ正直エラーメッセージでググれば解決できるのだけれど、英語ばっかりだったし、メモ。

zshbashを使っていると出ないエラーみたいなので、fishの関数が何やら悪さをしているっぽい?

ともあれ、

.vimrcの最初の方に

set shell=/bin/bash

と書いておけばおk

shellをvimrc読み込むときだけ、切り替えている

fish shellのconfig.fish

fishを使ってみた

f:id:g-hyoga:20160923232139p:plain

良さげなのでとりあえず、(自分は)これだけあればなんとかなるconfig.fishをメモしておく

fishはここから fish shell もしくは

brew install fish

とかで

config.fishは

~/.config/fish/config.fish

にかく

ないなら作る

config.fish

## 環境変数
set -x GOPATH $HOME/dev
set -x GOROOT /usr/local/go
set -x PATH $PATH $GOROOT/bin
set -x PATH $PATH $GOPATH/bin

## vi mode
fish_vi_key_bindings

# vi modeではなんか[I]みたいなの出るからオーバーライド
function fish_mode_prompt 
end

## prompt
function fish_prompt 
    if test "$fish_key_bindings" = "fish_vi_key_bindings"
        switch $fish_bind_mode
            case default
                set_color --bold red white
            case insert
                set_color --bold green white
            case replace-one
                set_color --bold green white
            case visual
                set_color --bold magenta white
            end
        echo (prompt_pwd) ">><(( *>  "
    end
end

#gitのbranch名出す
function git_branch
    git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ [\1]/'
end

#右prompt
function fish_right_prompt
    echo (git_branch)
end



## cd後にls 
function cd 
    builtin cd $argv; and ls
end

## rmでゴミ箱に入れる
alias rm='rmtrash'

function hello
    echo 'hello'
end

留意点

環境変数

環境変数の設定がなんか他のshellと違ってて、

set -x PATH hoge

てする

vi モードでのプロンプト

左プロンプト

さすがfish、vi モードがある. Vim大好きな私には嬉しい. ちなみにデフォルトはemacs

fish_vi_key_bindings

と書くとviモードになる。 f:id:g-hyoga:20160923232844p:plain すると最初は横に[I]とか[V]とかでる。 わかりやすいけど、ダサい

せっかくなので、左プロンプトをお魚さんにして、色でinsertやらを判断するようにした

function fish_prompt 
    do something
end

ここに書けば左プロンプトを操作できる

viモードに関してはgithubのコードをみました ここのfish_vi_nantoka

prompt_pwd

というのは、fishが用意してくれている関数で、デフォルトのプロンプトでも使われている。

/hoge/foo/bar

となっているのを

/h/f/bar

としてくれる、便利

右プロンプト

function fish_right_prompt
    do something
end 

ここに書く

これ をみました。 ありがとうございます

まとめ

何をしたかというとpromptの設定がほとんどで、 fishなだけあって、何もしなくても補完やらなんやらしてくれる、強い