コマンドラインでシェルのエイリアスと関数を使って作業を効率化する

 GNU/Linuxシステムを最大限に活用するには、やはりコマンドプロンプトとシェルスクリプトへの習熟が欠かせない。とはいえ、そうした処理はなるべく手早く済ませたいものだ。本稿では、コマンドラインでの作業の手間を最小化してくれる強力な方法として、シェルのエイリアスと関数を紹介する。

 ここではbashを例にとって説明するが、以下に示す概念のすべてとコードの大半はその他のシェルでも通用する。

 シェルのエイリアスとは、頻繁に実行する長いコマンドを覚えやすくするために付ける別名のことである。コマンドラインから入力された文字列に対し、シェルはまず設定済みエイリアスの一覧とのマッチングを行う。一致するものがあれば、そのエイリアスは対応するテキストに置き換えられ、結果として得られるコマンドライン全体の評価と実行が行われる。以下に、エイリアス定義の例を示す。

    alias sqlmanage='mysql -p -u my_MySQL_userid MySQL_database_name'
    alias ssh2rws='ssh -p port_number my_remote_userid@my.remote.web.server'
    alias findbig='find . -type f -exec ls -s {} \; | sort -n -r | head -5'
    alias cof='clear;ls -lrt'

 最初の2つはそれぞれローカルのMySQLデータベースとリモートサーバへの接続用コマンドを短縮して置き換えるもの、3つ目はカレントディレクトリからサイズの大きい上位5つのファイルを見つけ出すものである。4つ目のエイリアスは、ほかの3つよりもシンプルで短いが、最も融通が効く。エイリアス置換のしくみにより、この「cof」を次のように使えばファイルまたはディレクトリの一覧を古いものから順に表示できる。

    cof              # スクリーンをクリアして、カレントディレクトリの一覧を表示
    cof /tmp /usr    # スクリーンをクリアして、/tmpと/usrの一覧を表示
    cof *.jpg        # スクリーンをクリアして、カレントディレクトリにある.jpgファイルの一覧を表示

 エイリアスの定義はシェルのセッションの途中でプロンプトから行えるが、システムまたは個人用の設定ファイル(「/etc/bashrc」または「$HOME/.bashrc」)に記述しておけば、セッションごとに定義し直す必要がなく便利である。また、使っているシェルでどんなエイリアスが定義されているかを確認するには、プロンプトの後に「alias」とだけ入力すればよい。

 このように便利なエイリアスだが、制限事項や問題点がいくつかある。まず、適切なオプション(bashのmanページを参照)を設定しない限り、シェルが対話モードのときには使えない。また、シェルは、少なくとも完全な1行分の入力を読み込んだうえでエイリアスの置換を行い、結果として得られるコマンド全体を実行する。そのため、複数の定義済みエイリアスを同じ行で使うことはできても(ただし、意図したとおりの実行結果にならない場合がある)、同じ行のなかでエイリアスの定義と利用を行うことはできない。同様の理由により、シェル関数の内部でエイリアスを定義しようとすると問題が起こりうる。こうした問題を防ぐには、エイリアスを定義する行ではそれ以外のことを一切行わないようにしないといけない。つまり、複合コマンドや条件に応じて実行されるコードブロック内でのエイリアスの利用や定義は、避ける必要があるわけだ。

エイリアスよりも強力なシェル関数

 エイリアスによる置換は、定義したものと厳密に同じコードに対してのみ有効であり、類似のコードには適用されない。そのため、エラー処理やカスタムの終了コード生成によるフロー制御が行えず、変数も利用できない。こうした処理が必要になってきたら、シェル関数の使い方を覚えるとよい。

 シェル関数は、完全なサブスクリプトとして構造化されたコードブロックであり、いったん記述しておけば必要になったときに任意のスクリプトから呼び出すことができる。bashで関数を定義するには、以下の2つの方法がある。1つはfunctionキーワードを使うもので、もう1つはこのキーワードの代わりに丸かっこを使うものだ。

  function my_function { code }
  my_function () { code }

 どちらの構文を使うにせよ、シェル関数は呼び出しの前に定義しておく必要がある。関数の入れ子(ネスト)や再帰呼び出しにも対応しており、関数内ではそのシェルで利用可能な構造体のほとんどが使える。ただし、関数のコードでは、その関数を利用するスクリプトとの間でどのようなやりとりをするかに注意する必要がある。

 スクリプトで関数を利用できるのは、その関数が定義済みでそのスクリプト内であらかじめ宣言されているか、そのスクリプトを起動するシェルにおいて既知のものである場合に限られる。関数の作成と読み込みは、(概念的に)少なくとも4つの方法で行える。1つはスクリプトに書くのと同じようにコマンドラインから入力する方法であり、その定義を永続的なものにする方法として、すべての関数をファイルに記述して保存し、コマンドラインまたは任意のスクリプトから「.」コマンドでそのファイルを読み込むというやり方がある。

 また、シェルプロンプトのカスタマイズによって、シェルプロンプト自体の内部で関数を定義するという意外な方法もある。さらに次のように、同一の関数に対して複数のバージョンを用意し、ほかの変数の値によって動作を変えることも可能だ。

  if [ "$USER" eq "root" ]
    then
      . /etc/root_only_functions.sh
    else
      . /etc/normal_users_functions.sh

    fi
    backup_all_files # depends on user

 この「backup_all_files」関数は、スクリプトがrootで実行された場合には「/etc/root_only_functions.sh」に記述された内容が、その他の場合には「/etc/normal_users_functions.sh」に記述されたものが実行される。ここで1つ注意したいのは、同じ関数の定義が2回以上行われている場合は最後のものが呼び出される点だ。

 関数に引数を渡すには「$1」、「$2」といった名前の特殊な変数を用いる。すべての入力引数は「$@」という配列に格納される。ただし、「$1」、「$2」といった変数は、関数の引数の個数が決まっている場合にのみ利用すること。呼び出すたびに引数の数が変わりうる場合は、次のような関数を利用して配列「$@」から引数を1つずつ取り出して処理する。

bashのドキュメント
 bash関連の資料としては、「Bash Reference Manual」と「Advanced Bash Scripting Guide」が参考になる。
    function partition_maintenance {
        for partition in $@
        do
        #do something to the current partition
        done
    }

 また、どの関数でも、動作の診断に役立つように終了コードを返すようにする。そのためには、次のようにreturnキーワードを用いる。

    function check_treshold {
    if [ $SOME_VAR -gt 40 ] ; then echo 5; return 5; else echo 4; return 4 ; fi
    }

 SOME_VARの値が40以下の場合、この関数の戻り値は4となる。ただし、戻り値には数値しか使えないことに注意が必要であり、文字列や配列を返すには結果をechoで出力するか、次のようにバッククォートで囲んで代入を行う。

    IS_SOME_VAR_LESS_THAN_40=`check_treshold`

 あるいは、その変数の参照を関数に渡してもよい。その場合はevalステートメントを使って次のように記述する。

    function set_variable {
      eval "$1=$2"
    }

 この例では、「set_variable WEBSITE linux.com」というコマンドによって(変数名の前にドル記号がないことに注意)、$WEBSITEに「linux.com」が代入される。

 シェル関数を使って作業を効率化するには、シェル関数を使うべきでない状況についても知っておく必要がある。以下の簡単なシェルスクリプトは、関数を使わずに計算を行う3つの方法と、実際には計算が行われない1つの例を示している。

  #! /bin/bash

  MY_VAR=40
  echo 0: $MY_VAR
  MY_VAR=`expr $MY_VAR - 10`
  echo 1: $MY_VAR
  MY_VAR=$(($MY_VAR + (5**2 /2)))
  echo 2: $MY_VAR
  let MY_VAR="$MY_VAR + 7"
  echo 3: $MY_VAR
  MY_VAR="$MY_VAR + 7"    # this is a string assignment, not a calculation
  echo 4: $MY_VAR

 次の出力結果から、関数は確かにすばらしいものだが、簡単な算術演算であれば関数を使わなくても十分に間に合うことがわかるだろう。

  0: 40
  1: 30
  2: 42
  3: 49
  4: 49 + 7

Marco Fiorettiは「The Family Guide to Digital Freedom」の著者。普段はLinux.comをはじめとするIT関連誌に寄稿している。

Linux.com 原文(2008年9月16日)