Λlisue's blog

つれづれなるままに更新されないブログ

シェルスクリプトを書くときの頻出コードメモ

Linuxを使っているのでインストールや環境構築でシェルスクリプト (bash )をよく書きます。 シェルスクリプトだと様々な表現が可能なのですが、他言語と違い適当にかけないことが多々あるのでよくググる頻出コードをまとめておきます。

起動スクリプトのパスを取得

インストールや環境構築で使うことが多いのでスクリプトファイルからの相対パスを使いたい場合が多いです。Pythonであればos.path.dirname(__file__)にて簡単にとることができるのですがシェルスクリプトの場合は下記のようになります。

# 一行で書く方法
ROOT=$(cd $(dirname $0);pwd)

# わかりやすい書き方
ROOT=`dirname $0`
ROOT=`cd $ROOT;pwd`
 
# 使用例(スクリプトファイルと同位置にあるvimrcを~/.vimrcにリンク)
ln -sf $ROOT/vimrc $HOME/.vimrc

ファイル・ディレクトリ・アプリケーションの存在確認

特にアプリケーションの存在確認が面倒

# ファイルの存在確認
if [ -f "~/.vimrc" ]; then
    echo "きゃー"
fi
if [ ! -f "~/.vimrc" ]; then
    # 別に先の if の else でいいけど否定の例として
    echo "Emacs使いですか、そうですか。念の為に vim をいんすとーるしておきますね。"
    yes | sudo apt-get install vim
fi

# ディレクトリの存在確認
if [ -d "~/.vim" ]; then
    echo "イケメンよ!"
else
    echo "・・・あれ?"
    echo "念の為に vim をいんすとーるしておきますね。"
    yes | sudo apt-get install vim
fi

# アプリケーションの存在確認
which vim > /dev/null 2>&1
if [ $? -eq 0 ]; then
    echo "抱いて!"
else
    echo "さようなら"
    echo "・・・"
    yes | sudo apt-get install vim
fi

# アプリケーションの存在確認(簡易版 - 逆になるので注意)
if type vim > /dev/null 2>&1 then
    echo "さようなら"
    echo "・・・"
    yes | sudo apt-get install vim
else
    echo "抱いて!"
fi

プラットフォームによる処理の分離

僕はLinux (Debian)とMacを使用しているのでパッケージマネージャなどを変更する必要があります。 そのような場合は下記のようにunameによりプラットフォーム分離を行なっています。

UNAME=`uname`    # -r でバージョン, -p でアーキテクチャにも使用可

if [ "$UNAME" = "Darwin" ]; then
    # Homebrewを使ってる前提ですね(笑
    brew install macvim
elif [ "$UNAME" = "Linux" ]; then
    # 本当はプログラムの存在等で分岐させるべき
    # だけど Debian しか基本的に使用しないので
    sudo apt-get install gvim
fi

ユーザーに入力を求める(Yes / No)

なにか続行してもいいですか?系を聞くときの頻出表現。ループさせることで変な値入れても再度聞きなおすようにしている。

# 関数で定義しちゃったほうが楽。
# シェルの返り値は基本的には終了コードと同一の扱いなので
# Yesのときは0、Noのときは1を返すようにしている。
# ちなみに文字列を返したい場合は print して `` で実行する。
# 別に返してるわけじゃないけどね!
 askYesOrNo() {
    while true ; do
        read -p "$1 (y/n)?" answer
        case $answer in
            [yY] | [yY]es | YES )
                return 0;;
            [nN] | [nN]o | NO )
                return 1;;
            * ) echo "Please answer yes or no.";;
        esac
    done
}

# 使い方
askYesOrNo "Vim をインストールしますか?"
if [ $? -eq 0 ]; then
    # シェルスクリプトの返り値は終了コードを取る`$?`で取得
    yes | sudo apt-get install vim
else
    echo "Emacs派ですか、そうですか。念のためインストールしておきますね。"
    yes | sudo apt-get install vim
fi

複数の番号から処理を選択させる場合

  1. Vimをインストール
  2. GVimをインストール
  3. Emacsをインストール

のように番号でインストールしたいものを選ばせる場合などは下記のようにすればOK

while true; do
    cat <<EOF
Which editor do you want me to install?
 
1. Vim
2. GVim
3. Emacs
4. Nano
 
EOF
    read ACTION
    case ${ACTION} in
        1)
            yes | sudo apt-get install vim
            break;;
        2)
            yes | sudo apt-get install gvim
            break;;
        3)
            yes | sudo apt-get install emacs
            break;;
        4)
            yes | sudo apt-get install nano
            break;;
        *)
            echo "Please input the number";;
    esac
done

配列に追加していき一気に処理する

インストール候補を選択させて最後に一気にインストールとかするときに使う。 シェルスクリプトの配列は若干面倒…

INSTALL_LIST=("vim")  # vim は初期値、空でもOK `INSTALL_LIST=()`

askYesOrNo "Emacsをインストールしますか?"
if [ $? -eq 0 ]; then
    INSTALL_LIST=("${INSTALL_LIST[@]}" "emacs")
fi

# zshを追加 (2013-07-08 10:00 修正)
INSTALL_LIST=("${INSTALL_LIST[@]}" "zsh")

# (ry

# 全部インストール(例なのでforで回してみる)
for INSTALL in ${INSTALL_LIST[@]}; do
    yes | sudo apt-get install $INSTALL
done

(番外)GithubのREADMEとかで一行インストールコマンドを書く

Debian系(wget存在)の場合

% wget $URL -O - | bash

Darwin系(curl存在)の場合

% curl $URL | bash

なお記事全体を通してEmacsをdisってるように見えるかもしれませんが気のせいです。 筆者はEmacsを使ったことがないのでdisれません。ごめんなさい。