Shellの関数内でインタラクティブに得た文字列を戻り値として利用する方法

結論から述べると、「標準エラー出力を利用する」が答えである。が、備忘録もかねてその答えにたどり着いた経緯も含めて以下に記す。

Shellにおける関数では戻り値をとる場合、return文が利用できる。しかしこのreturn文、引数に0-255の正数しかとる事ができない。しかもこの戻り値を取得するには、関数を呼び出した後に$?変数(特殊変数)にアクセスする必要がある。

$ cat hoge.sh
#!/bin/sh

#
# define fuction.
#########################################
hoge(){
    ### do something...
    if エラー発生; then
        return 1
    else
        return 0
    fi
}

#
# main.
#########################################
hoge

if [ $? -ne 0 ]; then
    echo "エラー!!"
else
    echo "正常終了"
fi

高級言語になれている人は、とても扱いづらいと思うのではないだろうか。というか、自分は扱いづらいと思った。

でも実際には文字列を返すテクニックも存在する。ネットを調べるとすぐに出てくるが、「関数呼び出しをバックコートで行い、echo」を利用するというものだ。

$ cat fuga.sh
#!/bin/sh

#
# define fuction.
#########################################
fuga(){
    ### do something...
    if エラー発生; then
        echo "エラー!!"
        return 1
    else
        echo "正常終了"
        return 0
    fi
}

#
# main.
#########################################
msg=`fuga`

echo $msg

これができれば全て解決、問いいたいところだが、このfuga関数内ではmsg変数に代入したい文字列以外標準出力する事ができない。

しかし変数に代入される文字列は「標準出力されたもの」が対象である。従って「標準エラー出力」をうまく利用すれば、関数内でインタラクティブに文字列を取得し、戻り値として返す事ができるのではないかと考えた。

$ cat piyo.sh
#!/bin/sh

#
# define fuction.
#########################################
decho(){
     echo "$1" 1>&2
}

get_string(){
    decho "input> \c"
    while read val; do
        if [ "$val" != "" ]; then
            break
        fi
        decho "input> \c"
    done
    echo "$val"
}

#
# main.
#########################################
return_val=`get_string`

echo $return_val

以下実行結果。

$ ./piyo.sh
input> <Enter>
input> <Enter>
…
input> piyo
piyo

上記の手法により標準入力から任意の文字列を取得し、表示するといった処理を関数内にまとめ、戻り値に取得した文字列を渡すという処理が実現できた。 (サンプルではshellのようにEnterのみが押された場合は、処理を終えずに次の入力待ちをするようにしている。)

以下のように出力を使い分ける事がポイントだ。

  • 標準エラー出力 : 対話用の出力用として利用
  • 標準出力 : 戻り値用として利用

ネットを調べても上記の方法を紹介しているサイトは見つからなかった。同じ悩みを持つ人の一助になればと思う。