シェルスクリプトで指定した範囲の数値 (range) を取得する

seq コマンドで簡単にできます。
以下のコマンドで1から5までの数字が改行区切りで得られます。

seq 1 5

seq コマンドを用いてループする場合、以下のようになります。

#/bin/bash -eu

end=5
for i in $(seq 1 ${end}) ; do
  echo $i
done

また別の方法で、Ruby の Range オブジェクトの作り方に近い方法もあり、以下のようになります。
結果は同じですが、範囲の数値を指定している部分を変数にすることが出来できないため不便かもしれません。

#/bin/bash -eu

for i in {1..5} ; do
  echo $i
done

シェルスクリプトでset -eしているときに処理を中断せずエラーを扱う方法

シェルスクリプトで set -e しておくと、実行したコマンドの戻り値が非0で失敗したとき、そこでスクリプトの処理を中断してくれます。

エラー判定の処理を書かなくてもよくなるので、書き捨てるスクリプトを書くときなどとても便利ですが、エラーハンドリングして何かをしたい場合は意図せず処理が中断されてしまい困ったことになります。

例えば以下の場合、false コマンドの戻り値は1(失敗)なのでそこで処理は中断され”finish”の出力は行われません。

#!/bin/bash -e
false
echo "finish"

-eオプションで困ってしまうケースの例

例えば grep コマンドの戻り値は、検索結果が見つかれば0(成功)、そうじゃなければ1(失敗)となりますが、検索結果が見つからなかったときも続きの処理を行いたい場合は困ったことになります。

以下の例は、grep コマンドの検索結果が見つからなかった場合にそこで処理が中断されてしまい、続きの処理を行えません。

#!/bin/bash -e
grep "unmatch pattern" hoge.txt
echo "return value: ${?}"

この対処法を4つメモしておきます。

対処法1: コロンコマンドを使用する (:コマンド)

#!/bin/bash -e
grep "unmatch pattern" hoge.txt && :
echo "return value: ${?}"

→ return value: 1

解説

これは-eオプションにおける特殊な特性を利用した方法です。
コロンの部分はひとつのコマンドで、何もせず0(成功)の戻り値を返すコマンドです。

式の評価の流れ:

  • grep コマンドの戻り値が0(成功)だった場合、&& 演算子により右側も評価されコロンコマンドを実行。一連のコマンドの戻り値は0(成功)。←ややこしい^^;
  • grep コマンドの戻り値が1(失敗)だった場合、&& 演算子により処理はそこで中断。なので一連のコマンドの戻り値は1(失敗)。

set -eオプションは && や || で結合された式リストの最後のコマンドが1(失敗)だった場合のみスクリプトを中断するという特性があるためそのままスクリプトは続行されます。$?にもちゃんと grep の戻り値が入っています。

なのでちょっと直感的ではないですが、上記の特性により以下のコマンドの出力は“route1″のみとなります。

#!/bin/bash -e
false && false
echo "route1"

false
echo "route2"

対処法2: trueコマンドを使用する

grep "unmatch pattern" hoge.txt && true
echo "return value: ${?}"

→ return value: 1

解説

対処法1と同じ理由です。true コマンドもなにもせず0(成功)を返すコマンドです。

対処法3: 戻り値を取得せずに処理を行う

#!/bin/bash -e
if "pattern" hoge.txt ; then
  # "Matched case, return value is 0."
  echo "return value: ${?}"
else
  # "Unmatched case, return value is 1."
  echo "return value: ${?}"
fi

解説

この方法は一番シンプルに解決できる方法かもしれません。
これも-eオプションにおける挙動の特性を利用しており、if の評価式の戻り値が1(失敗)の場合でも処理は中断されないという特性があるため処理を続行できます。

対処法4: 一時的に set +e して -e オプションを解除する

#!/bin/bash -e
set +e
grep "unmatch pattern" hoge.txt
echo "return value: ${?}"
set -e

→ return value: 1

解説

個人的に少しダサさを感じてしまいますが、この方法が楽そうならこれで。最後にset -eで元に戻すのを忘れずに。

まとめ

対処法3のケースなど普段何気なく実現できていたことも、ちょっとした挙動の特性により実現出来ていたことが理解できました^^

この記事は bash を前提としていますが、他のシェルの場合の挙動はどうなるか分かりません。

個人的にはコロンコマンドを使用した方法がシンプルで好きですが、時と場合で使い分けで。

参考資料

phpMyAdminでセッションタイムアウトの時間を伸ばしても反映されない

よく phpMyAdmin でログインセッションの有効期限を伸ばす方法として、config.inc.php に以下の設定を追記する方法が紹介されてます。

$cfg['LoginCookieValidity'] = (60 * 60 * 4); // 4 hours
ini_set("session.gc_maxlifetime", $cfg['LoginCookieValidity']);

この設定を Ubuntu 上の phpMyAdmin に設定してみたのですが、どうも設定した時間より早くログインセッションが切れてしまうみたいで、なんでかなーと調べていたら原因が分かったのでメモしておきます。

原因

Debian 系の OS で php5 をインストールしたときに設定されている、古いセッションファイルを定期削除する cron が原因でした。

Ubuntu だと session_start() したタイミングで GC が実行される確率 (session.gc_probability) の値が 0 として設定されており無効になっています。その代わりとして設定されている cron です。

参考: ubuntuではphp.iniのsession.gc_probabilityを変えてはいけない

定期セッションファイル削除時の動作

/etc/cron.d/php5 にこのような設定がされていました。

09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -x /usr/lib/php5/sessionclean ] && [ -d /var/lib/php5 ] && /usr/lib/php5/sessionclean /var/lib/php5 $(/usr/lib/php5/maxlifetime)

毎時間の9分と39分に /var/lib/php5 に保存されている古いセッションファイルを探して削除するような設定です。

ざっくりな削除処理の流れとしては
1. /usr/lib/php5/maxlifetime のスクリプトで、各 /etc/php5/*/php.ini に設定されている session.gc_maxlifetime の時間で最も大きい時間を取得(このときの単位は分)
2. /usr/lib/php5/sessionclean のスクリプトで、 /var/lib/php5 以下にあるセッションファイルの内、1で取得した時間より古くなっているセッションファイルを削除
という感じでした。

なので、いくら ini_set 関数で session.gc_maxlifetime の時間を変更したところで、その時間は考慮されずに定期削除されてしまいます。

$cfg['LoginCookieValidity'] = (60 * 60 * 4); // 4 hours
ini_set("session.gc_maxlifetime", $cfg['LoginCookieValidity']); // ← 考慮されない

phpMyAdmin に限った問題ではないですね。

解決方法

対処法の例を2つ載せますが、どちらの場合でも phpMyAdmin の config.inc.php で LoginCookieValidity の時間の設定しておく必要があります。

解決法その1: session.save_path を変更する

cron が定期削除する対象は /var/lib/php5 ディレクトリ決め打ちなので、セッションファイルの保存先を変更してしまえばこの問題は起こりません。

phpMyAdmin/.htaccess:

php_value session.save_path /tmp/phpmyadmin

上記のような設定の場合、あらかじめ phpmyadmin ディレクトリは作成しておく必要があります。

mkdir /tmp/phpmyadmin
chmod 777 /tmp/phpmyadmin

解決法その2: php.ini の gc_maxlifetime の値を伸ばす

この方法の場合、他に稼働しているシステムに影響するかもしれないので注意が必要です。

/etc/php5/apache2/php.ini:

session.gc_maxlifetime = 14400

設定し終わったら以下のコマンドを実行して反映されているか確認してみるといいと思います。

 
/usr/lib/php5/maxlifetime

紛らわしいですが設定時の単位は秒で、確認時の単位は分で表示されます。