/var/log/Sawada.log

SAINO中毒患者の備忘録。

dotfilesのsetup用スクリプトを書いた

はじめに

今更...な記事ですが github で dotfiles を管理しています。今まで個別にシンボリックリンクを張ったりしていたのですが、シェルスクリプトで纏めたいと思ったのがいつの間にか沼へずぶずぶ。

github.com

目次です。

dotfilesとは

Windows派にはあまり馴染みのない言葉ですが、dotfilesとは LinuxMacOS における設定ファイルの事です。大学1年のに Linuxリテラシー演習にて、ホームディレクトリにあったログインシェルの.bashrcを弄り倒したのがきっかけで「設定書くのが楽しいっ!!!!」となりました。高まる Windows はクソという感情も最近はWSLによって緩和されつつあります...

きっかけ

自宅の仮想環境や大学の演習端末、OSを吹き飛ばす度に自分好みの開発環境まで一括設定するスクリプトの必要性を感じ始めました。特に、ラップトップの代替わりや HDD or SSD 故障時のスムーズな移行のためにもdotfilesの管理はおすすすめです。理想的な機能はこんな感じ。

  • 秘伝のタレたる.dotfilesを適所に配置するシンボリックリンク
  • OS 毎に足りないパッケージをインストール
  • 対話処理: インストールの必要性を最初に確認(yes/no)

github で設定ファイルをどうやって管理するの?という人は、先人の知恵がはてなブログや Qiita に溢れているのでそちらを参照してください。では以下がスクリプトの解説です。

setup.sh の中身

端末にセットアップ状況を表示するためにちょっとコードが長くなってますが、大まかな記述の流れは、

  1. インストールパスの設定

  2. 標準出力の装飾設定

  3. 関数定義

  4. OSで条件分岐して必要パッケージがない場合はインストール

  5. シンボリックリンクをドヴァァッ

こんな感じです。リポジトリのれあどめにも書いてますが、curlが事前にインスト―ルされていればワンライナーで楽にインストールが可能です。自分のターミナル上で

$ bash -c "`curl -fsSL https://raw.githubusercontent.com/takuzoo3868/dotfiles/master/setup.sh `"

と打ち込むと、わざわざリポジトリgit cloneしなくても実行してくれます。シェル芸ですね。homebrew のインストール方法を流用した形になります。bash -cは直後の文字列を命令として読み込むためのオプションです。これによりcurlsetup.shの中身を取得すれば実行できるという訳です。curl側のオプションはこんな事をしています。

-f, --fail:
    サーバーエラーが起こった時に何も出力しないで終了
-s, --silent:
    silentモードとなり進行状況を出力をしない
-S: --show-error:
    silentモードと同時使用でエラーのみ表示
-L: --location:
    ページがリダイレクトされてる場合にはリダイレクト先まで取得しにいく

読み込みエラー防止のためのオプションと捉えて間違いないかと思います*1

文字色の設定

# use colors on terminal
tput=$(which tput)
if [ -n "$tput" ]; then
  ncolors=$($tput colors)
fi
if [ -t 1 ] && [ -n "$ncolors" ] && [ "$ncolors" -ge 8 ]; then
  RED="$(tput setaf 1)"
  GREEN="$(tput setaf 2)"
  YELLOW="$(tput setaf 3)"
  BLUE="$(tput setaf 4)"
  BOLD="$(tput bold)"
  NORMAL="$(tput sgr0)"
else
  RED=""
  GREEN=""
  YELLOW=""
  BLUE=""
  BOLD=""
  NORMAL=""
fi

一番最初に困ったのは、echo -eによる文字色の出力について escape sequence が効かない状況でした。シェル環境によって、或いは shebang#!/bin/shの時などはPOSIXが有効なのでechoも機能が制限されます*2。故に escape sequence を書いてもそのまま端末に表示されちゃいます。printfを使うと解決できたりしますが escape sequence が途中でよく分からなくなってしまうので、ここでは便利なtputを活用します。tputとは escape sequence とターミナル機能の関連付けなどが定義されたterminfoを利用して、依存情報をシェルでも使えるようしてくれる優れたコマンドです。setafで指定した色番号に文字色を変更できます。boldは太字化、sgr0は文字装飾をリセットして標準に戻します。スクリプトでは上記のように文字色を変数定義した上で、文字出力を以下のように区別する事にしました。

# info: output terminal green
info() { 
  printf "${GREEN}"
  echo -n "  info  "
  printf "${NORMAL}"
  echo "$1"
}

# error: output terminal red
error() {
  printf "${RED}"
  echo -n "  error "
  printf "${NORMAL}"
  echo "$1"
}

# warn: output terminal yellow
warn() {
  printf "${YELLOW}"
  echo -n "  warn  "
  printf "${NORMAL}"
  echo "$1"
}

# log: out put termial normal
log() { 
  echo "  $1" 
}
  • info: 正常な進行状態、進捗を表示

  • error: 異常終了するときやエラーメッセージの表示

  • warn: ユーザーの同意が必要な警告表示

  • log: 色なんて要らねぇ、ただ表示しろ用

この辺の考え方はCUIベースのOSSにも応用できそうな気がしますがどうなんでしょう。自分はbio/AA芸人だと思ってるのでdotfilesにもAAを入れました。先ほどのワンライナーコマンドを叩くとこんな感じで表示されます。

f:id:takuzoo3868:20171029025454p:plain

対話応答

read -p "$(warn '(U^w^) < Are you sure you want to install it? [y/N] ')" -n 1 -r


if [[ ! $REPLY =~ ^[Yy]$ ]]; then
  echo ""
  error 'Installation failed. Nothing changed.'
  exit 1
fi

echo ""
info "Start install the dotfiles."

本当に実行しちゃっていいの?みたいな確認は必要ですよね。シェルスクリプトの場合はreadで実装できます。入力待ちにおける yse/no の判断は以下のオプションを使用しました。

-p:
    プロンプト表示、入力待ち状態となる
-n:
    引数に入力する文字数の指定
-r:
    エスケープの無効化

デフォルトではYy以外の入力があった場合、異常終了するように処理しています。よってそのままEnterを押してもnと解釈されるので異常終了します。一方、条件に当てはまらない場合はそのままインストール処理へ移行します。

OSの条件分岐

以前UNIX系統のディストリビューション判定のスクリプトを書いたのでそれを流用しました。

## Android
if [ $(uname -o) = "Android" ]; then
  
  # ...

elif [[ $(uname) = "Linux" ]]; then

  ## Arch Linux
  if [ -f /etc/arch-release ]; then

    # ...

    ## Ubuntu / Debian
  elif [ -f /etc/debian_version ] || [ -f /etc/debian_release ]; then
    
    # ...

  fi

## MacOS
elif [[ $(uname) = "Darwin" ]]; then

  # ...

else
  # F0ck wind0ws. G0 t0 he11!
  error "Your platform ($(uname -a)) is not supported."
  exit 1
fi

基本的にunameコマンドで対応できますが、Linuxディストリビューションの違いは/etc/hogehoge-releaseを参照する必要があります。この辺の処理部分の記述は無駄が多いのでまだまだ改善の余地がありそうです。今は自分が触ってるOSだけ条件式に追加していますので、他のディストリビューションの場合はerrorを表示して異常終了します。一応、 Termux on Android にも対応させるつもりで書いてますが、アレは特殊な環境なので shebang をどうにかしないと何とも言えない感じですね...。

おわりに

たかがdotfilesのインストールかもしれませんが、設定すると結構奥が深いです。完全にシェル沼です。今後の課題としては OS 毎のインストール記述が煩雑になってきたので別のスクリプトで管理しようかと考えています。現状はシンボリックリンクも強制的に貼り付けなので、バックアップファイルを作成して元々あるdotfilesを一時退避させる必要もあると思います。あとは、MacOS の記述が手元に MacMini を置いていた時のものなので改善できそうな気がします...。今度はneovimやgitの設定を記事に書いて晒します()