2016年4月21日木曜日

Raspberry PiのLinux、Raspbian atコマンドで学んだshとbashの問題

Raspberry Pi Zeroが日本国内販売はおろか、本家イギリスでも相変わらず入手困難な状況が続いているようです。
そんな中、突如発表発売されたRaspberry Pi 3
ARM Coretex-A53を採用したSoC Broadcom BCM2837を搭載し、Raspberry Pi初となる64ビット機となります。また、無線LANBluetoothも搭載されコレも初。

今後も目が離せませんね♪
Linux三昧
Raspberry Pi 2を購入して以来その魅力に取り憑かれてしまい、オフィシャルOS Raspbianを通じてLinuxが楽しくて仕方ない今日このごろ。
こんな事したら楽しいかな?あんな事したら便利かな?を形にすべく、時間を見てはチョコチョコいじっています。

そんなLinuxには色々便利機能があるのですが、その中の1つにatコマンドというのがあります。

Raspbian標準では入ってなくて、Debian向けに開発されたパッケージ管理システム APTを通じてインストールする事が可能です。

$ sudo apt-get install at

こんな感じでコマンドラインから打ってやるとインストールできます。
atコマンドで出来る事
そもそも、このコマンドで何が出来るか?というと

指定した日時に1度だけ実行できる

Raspbian標準でcrontabという似たコマンドがあり、やろうと思えばこちらでも同様の事が可能です。
しかし、crontabでatと同じ事をしようとした場合、

実行し終わったらスケジュールリストから自力で削除しなければならない

crontabはシステムが稼働中にスケジュール条件のジョブがあった場合に実行するコマンドです。
定期的に動かさなければならない場合に使う事が多いので、1回コッキリの作業にはあまり向かないコマンドです。
うまく動いてくれないジョブ
便利なatコマンドは何度も使っていて、勝手知ったるツモリでこの日もパチパチ作っていました。
登録するジョブのシェルスクリプトを手動でテスト実行したら想定通りの動作。
いざ、本番を想定したatコマンド登録して1分後に実行するようセット。


・・・
・・・
・・・

ただのシカバネのようだ。


ウンともスンとも言いません。
何か間違ってるのかと思い、手動実行するもちゃんと動きます。
実行ユーザーの差も疑ってsudo付けて実行しましたがやっぱりちゃんと動きます。
難航するデバッグ作業
atコマンドは通常は標準出力に文字を表示してくれません。
何かあればメールでメッセージが入ってくる事があるのですが、今回はそれもありません。
今回実行しようとしたシェルスクリプトは、sourceコマンドで別の関数をライブラリ化したシェルスクリプトを読み込んで使うモノでした。
それが上手く動作していないのかと思ったのですが見当ハズレ。
単独で動いた簡単なコード

echo "test" > out.log

これ1行のみのコードの関数を作り、それをsourceコマンドで読み込んでメインスクリプトで実行すると上手くいきます。

もう何が悪いのかわからないヽ(`Д´#)ノ

問題箇所を洗い出すべく、ヤケッパチの作戦に出ました。
すべての行間に

echo "test(任意の数字)" > out.log

これを入れ込んでって、out.logの出力がどこまで動いてるかを見て、停止原因となる箇所特定をしました。
まさかの配列変数初期化で停止
問題箇所はなんてこと無い1行でした。

array=()

シェルスクリプトではなんてことの無い、単に配列を初期化するだけの1行で停止していました。
もちろん、手動で実行すると動いていますから問題がある書き方ではありません。
declare -aという変数宣言コマンドも使ってみましたが動きません。
atコマンドが発するwarningの意味
atコマンドを実行すると良く見かけるのがコレ

warning: commands will be executed using /bin/sh

意味は /bin/shを使って実行されます です。
エラーでは無いためatコマンドは実行してくれるのですが、この警告がとても重要なのです。
Raspberry Pi オフィシャルOSである Raspbianは、標準のシェルとしてbashが使われています。
bashはとても高機能なので幅広く使われてるシェルプログラムなのですが、それも今のようなコンピュータが高性能になったおかげです。
それ以前は、性能の低いコンピュータに合わせたモノが使われていました。
atコマンドの警告表示に出ている/bin/shというのがまさにその使われていたshなのです。
Rasbianでこのshを見てみると

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4  1月 21  2014 /bin/sh -> dash

エイリアスでdashが呼ばれていました。
bashdash、1文字違いでヤヤコシイのですがどちらもシェルプログラムです。
両者同じものならわざわざ別に用意するワケがなく、もちろん別のシェルプログラム。
shとの互換性があるのがdashです。
互換性のないbash
動作しなかった配列変数を調べてみると、bashにはあってdash/shには無い機能の1つでした。
atコマンドが使うシェルプログラムの指定は出来ないのか調べてみましたが、それっぽい記述は見当たりません。
シェルスクリプト側を配列を使わない形に書き換えようとも考えたのですが、パッと良いアイディアが浮かびません。
そこで取った解決方法はコレ

#!/bin/sh
/bin/bash hogehoge.sh

bashを呼び出してシェルスクリプトを実行させる方法でした。
perl/ruby/python/php等のスクリプトも似たようにコマンドで呼び出して実行します。
bash向けシェルスクリプトもそれらと同様の方法で実行しました。
atコマンドがshのワケ
bashdash、1文字違いのシェルプログラムですが、その志しはまったく異なります。

dashPOSIX準拠のシェルプログラムです。
shの機能そのままにコンパクトで高速に動作する事を目的に作られています。
dashで走らせられるシェルスクリプトは実行速度の差はあってもshで走らせられる事ができる互換性のあるモノです。

bashshを拡張して高機能化したシェルプログラム。
後方互換のモノで、shのシェルスクリプトを実行可能にしつつ、さらに便利な機能も実装しているというもの。
ですから、bashの機能を使ってbash向けに作られたシェルスクリプトはshでは動かない可能性があります。

atコマンドもまたPOSIX準拠で作られており、そのためにshを標準シェルプログラムとして使っているのでしょう。
bashの未来
atコマンドのトラブルから学んだ今回のsh/dashbashの問題。
シェルスクリプト冒頭に書く

#!/bin/sh

コレもbash向けシェルスクリプトでは書くべきではないと言われている事は以前から知っていたのですが、今回の一件でその理由が良くわかりました。

bashは今回のRaspbianだけでなく、UbuntuやOS Xでも採用されてるシェルプログラムです。
WindowsもWindows Subsystem for Linuxという形で2016年夏リリース予定のWindows 10メジャーアップデート時に搭載されます。

これだけ見ると未来は明るいように見えますが、一方でPOSIX準拠が取れない現状。
POSIXモードというのも実装されていますが、それを前提として開発するならbashの存在価値は失われてしまいます。

心の片隅にとどめておき、どちらに転んでも良いシステム開発に心がけたいですね。