ユーニックス総合研究所

  • home
  • archives
  • bash-func

Bashの関数の使い方: コマンドのように呼び出す関数の定義

  • 作成日: 2020-08-19
  • 更新日: 2023-12-24
  • カテゴリ: Bash

Bashの関数の使い方

Bash(バッシュ)というのはシェルでありスクリプト言語です。
このBashで書かれたスクリプトでは関数を使うことが出来ます。

処理が長くなったら関数に分割して処理を分けるのが定石です。
↓は「Hello!」と出力するBashの関数のサンプルコードです。

function hello() {  
    echo "Hello!"  
}  

hello  
# Hello!  

この記事ではBashの関数について具体的に↓を見ていきたいと思います。

  • 関数とは?
  • Bashの関数の作り方
  • Bashの関数の呼び出し方
  • Bashの関数の引数
  • Bashの関数の出力を代入する
  • Bashの関数の返り値
  • 関数内の変数のスコープ
  • 使用例

関数とは?

プログラミングにおける関数とは、処理のまとまりのことです。
長くなった処理(コード)を関数に切り分けることでプログラム全体の見通しが良くなって、プログラムの品質を上げることができます。

関数には入力と出力があります。
関数の引数(ひきすう)によって関数に入力を渡すことができます。
引数とは関数を呼び出す時に渡す外部からの入力のことです。

たとえばBashではfuncという関数が存在する場合、↓のようにfuncという関数を呼び出しますが、

func  

この時に関数funcの呼び出し時に渡すのが引数です。
引数はたとえば↓のように(1 2 3というのが引数)渡すことができます。

func 1 2 3  

関数の出力とは、関数の返り値と関数内の出力のことです。
関数を呼び出し、関数がいろいろな処理をやった後に、呼び出し側に返す値を返り値といいます。

Bashの関数の返り値は↓のように$?で参照することができます。

func  
if [ $? -eq 0 ]; then  
    echo success  
fi  

Bashは一般的なプログラミング言語の文法に比べると、だいぶ毛色が違う文法を持っていますが、これはBashがコマンドを中心に記述するシェルスクリプトだからです。
Bashはコマンドを使うことを第1に考えて設計されています。そのため他のPythonやRubyなどのプログラミング言語に比べると変わっています。
しかし慣れると合理的な文法になっていることがわかると思います。
この記事ではBashで使う基本的な機能と文法について記述していきます。

Bashの関数の作り方

Bashの関数は↓のフォーマットで記述します。

function 関数名() {  
    処理  
}  

最初にfunctionというキーワードを書き、その次に関数名を書きます。
関数名の後にはカッコ(())を書き、それからブレース(波括弧、{})を書きます。
関数名の前にあるfunctionは省略することが出来ます。
functionを省略した場合は↓のようなフォーマットになります。

関数名() {  
    処理  
}  

たとえばfuncという関数を定義してみると、Bashでは↓のように書くことが出来ます。

function func() {  
    return 0  
}  

return 0というのは返り値を返しています。
関数名にはアンダーバーやハイフン、大小英数字のほか、日本語も使うことが出来ます。

function あばば() {  
    return 0  
}  

function _abc-DEF123.456() {  
    return 0  
}  

🦝 < ハイフンやドットも使える!

Bashはコマンドを実行するためのスクリプトなので、コマンドの命名規則を関数名に流用することが出来ます。
コマンドの命名規則とは、つまりファイル名ということです。

Bashの関数の呼び出し方

Bashの関数は、コマンドのように呼び出します。
つまり、関数名の後にカッコをつけません。

function hi() {  
    echo "Hi"  
}  

# ↓ここで呼び出し  
hi  
# Hi  

なぜこのような関数呼び出しになっているのでしょうか。
他のコンパイラ言語やインタプリタ言語から来た人は頭にクエッションマークですよね。
これもBashがコマンドを中心に扱うスクリプト言語であることに由来します。
関数の呼び出しもコマンドのように行えるようにすることで、他のコマンド呼び出しと区別せずに使えるようになるんですね。
つまりBashにおいてはこっちが普通で、このほうが便利なわけです。

Bashの関数の引数

Bashの関数には引数を渡すことが出来ます。
これも普通のコマンドのように指定できます。

hi 1 2 3  

この引数を関数内で参照するには↓のようにします。

function hi() {  
    echo Hi $1  
    echo Hi $2  
    echo Hi $3  
}  

hi 1 2 3  
# Hi 1  
# Hi 2  
# Hi 3  

関数に渡された1番目の引数は$1で参照し、2番目は$2, 3番目は$3のように参照します。
Bashの関数の引数はこのように$に数字をつけて参照します。
他にも↓のような特殊変数を参照できます。

  • $0 ... ファイル名
  • $# ... 関数の引数の総数
  • $1 ~ $n ... 引数
  • $* ... 全引数リスト。「"」で囲むと"$1 $2 ..."のように展開される。
  • $* $? ... 最後に実行したコマンドの返り値

これらの変数を使ったサンプルは↓のようになります。

function allstar() {  
    echo ファイル名: $0  
    echo 引数の総数: $#  
    echo 1番目の引数: $1  
    echo 2番目の引数: $2  
    echo 3番目の引数: $3  
    echo 全引数リスト1: "$*"  
    echo 全引数リスト2: "$@"  
    echo 返り値: $?  
}  

allstar cat dog bird  

出力結果。

ファイル名: a.sh  
引数の総数: 3  
1番目の引数: cat  
2番目の引数: dog  
3番目の引数: bird  
全引数リスト1: cat dog bird  
全引数リスト2: cat dog bird  
返り値: 0  

$*$@の違いがわかりづらいですが、これはfor文で回してみるとよくわかります。

function hi() {  
    echo "\$*の場合"  
    for el in "$*"; do  
        echo $el  
    done  

    echo ""  
    echo "\$@の場合"  
    for el in "$@"; do  
        echo $el  
    done  
}  

hi cat dog bird  

出力結果。

$*の場合  
cat dog bird  

$@の場合  
cat  
dog  
bird  

$*for文で回すと全ての引数が1行の文字列として処理されますが、$@は引数ごとに処理されているのがわかります。

Bashの関数の出力を代入する

Bashの関数の出力は、関数名をバッククオートで囲むと左辺の変数に関数の出力を代入することが出来ます。

function hi() {  
    echo Hi  
}  

result=`hi`  
echo result: $result  
# result: Hi  

バッククオートの他には$()も使えます。

function hi() {  
    echo Hi  
}  

result=$(hi)  
echo result: $result  
# result: Hi  

バッククオートは入れ子にできませんが、$()は入れ子に出来る特徴があります。
拡張性を考えた場合は$()、手軽さならバッククオートになりそうです。

Bashの関数の返り値

Bashの関数は返り値を返すことが出来ます。
返り値を返すにはreturnを使います。
この返り値を参照するには、関数の実行直後に$?を参照します。

function dogs() {  
    return 101  
}  

dogs  
echo $?  
# 101  

関数内にreturnが無い場合はデフォルトで最後のコマンドの実行結果になります
↓の場合、fail_testの返り値に注目してください。

function fail_1() {  
    return 1  
}  

function fail_2() {  
    return 2  
}  

function fail_test() {  
    fail_1  
    fail_2  
}  

fail_test  
echo $?  
# 2  

返り値は0が正常終了、0以外が異常終了が慣例になっています。
Bashの関数は明示的、あるいは暗黙的に返り値を返しますが、これらの関数の組み合わせには&&を使うことが出来ます。

function one() {  
    echo "one"  
    return 0  
}  

function two() {  
    echo "two"  
    return 1  
}  

function three() {  
    echo "three?"  
    return 0  
}  

one && two && three  
# one  
# two  

↑の場合、two関数が異常終了の返り値(1)を返すので、最後のthreeは実行されずにコマンドラインが終了します。

関数内の変数のスコープ

関数内で定義した変数はデフォルトでグローバルスコープになります。
↓の場合、変数aは関数の外からも参照できます。

function hi() {  
    a=1  
}  

hi  
echo $a  
# 1  

変数をローカルにしたい場合は変数名の前にlocalを付けます。

function hi() {  
    local a=1  
}  

hi  
echo $a  
# 出力なし  

使用例

Bashの関数の使用例です。

findとgrepを抽象化する

findgrepを抽象化して関数にします。

function look() {  
    find "$1" -type f | grep "$2"  
}  

look /tmp "my-dust"  
# /tmp/my-dust.txt  

lsを抽象化する

lsを抽象化して、番号でファイル名を参照できるようにします。

function lsf() {  
    arr=(`ls "$1"`)  
    echo ${arr[$2]}  
}  

lsf /tmp 2  
# a.txt  

問題

Q1: Bashの関数の定義で、関数名の前に付けるものを答えよ

  1. func
  2. function
  3. fn

Q2: Bashの関数内の変数で、$#の変数の意味を答えよ

  1. ファイル名
  2. 最後に実行したコマンドの返り値
  3. 関数の引数の総数

Q3: Bashの関数で返り値を返したい時に使われる文を答えよ

  1. return
  2. echo
  3. find

答え

Q1: 2
Q2: 3
Q3: 1