Plan 9とGo言語のブログ

主にPlan 9やGo言語の日々気づいたことを書きます。

9legacy/9front/plan9portのrcシェルで`delim{cmd}構文が利用可能になりました

タイトル通りなんですが、主なPlan 9派生の rc シェルで `delim{cmd} 構文が使えるようになりました。この構文は9atomで最初に実装されたのですが、用途としては一時的に $ifs を置き換えたい場合に利用できます。

# 例です
newline='
'
a=`$newline{cat file}

$ifs を変更することに比べて、変更が子孫プロセスに影響するかどうかが異なります。$ifs環境変数*1なので子孫プロセスに引き継がれてしまいますが、この構文は書いたコマンドの出力にだけ影響します。

例えば、あまり良い例ではありませんが、次のような platform.rc ファイルがあったとします。実行するとOSとCPUアーキテクチャ: で区切って出力します。

#!/usr/lib/plan9/bin/rc

os=`{uname}
arch=`{uname -m}
switch($os){
case Linux
    echo -n linux:$arch
case *
    echo -n other:$arch
}

そして platform.rc を呼び出す build.rc があったとします。このとき、build.rcplatform.rc の出力を分割しようとして $ifs: に設定しています。

#!/usr/lib/plan9/bin/rc

# ifsを一時的に変更するため var=val {cmd} スタイルを利用している
ifs=: {a=`{rc ./platform.rc}}

os=$a(1)
arch=$a(2)

一見うまく動きそうですが、実際は親プロセスで変更した $ifsplatform.rc にも影響してしまい ifs=: として uname の出力を扱います。これでは uname の出力にある改行が残ってしまうため、意図した動作にならず問題ですね。この例では改行が残る程度ですが、もっとひどい結果になる場合はあるでしょう。

そこで `delim{cmd} では、環境変数$ifs は変更せずに、コマンド出力を分割する部分でだけ delim を使うようになっています。基本的に、$ifs を子孫プロセスに引き継ぎたいケースはほとんど無いと思いますし、今までの書き方よりもこちらの方が便利なので、積極的に利用すると良いんじゃないかなと思います。

# これまでの書き方
ifs=: {a=`{rc ./platform.rc}}

# 新しい構文では記述も簡単になる
a=`:{rc ./platform.rc}

*1:厳密にはUnix相当の環境変数ではないけれど分かりやすさのためにそう表現しています