10周年のSPコンテンツ!

シェルスクリプトにシバン(#!/bin/sh)はないほうがいいという説

職場のパソコンで作業用にシェルスクリプトを書いていた。PATH変数の初期化に使っていたgetconfコマンドがないことを発端に,getconfのない環境でのPATH変数の初期化から,Androidで動くシェルスクリプトについて議論が発展した。 その中で,Androidでも動くシェルスクリプトを書くためには,シバン(#!/bin/sh)を書いてはいけないという結論が得られた。 一連の議論を記録として残す。 続きを読む
プログラミング posixism posix原理主義 shell シェルスクリプト
8
Togetter投稿
. @senopen
「シェルスクリプトにシバン(#!/bin/sh)はないほうがいいという説」をトゥギャりました。 togetter.com/li/1077808
. @senopen
まとめを更新しました。 シバンがない場合,cshの対話シェルから起動されたら,shで再起動するにして,対応できたので,その話とサンプルコードを掲載。「シェルスクリプトにシバン(#!/bin/sh)はないほうがいいという説」 togetter.com/li/1077808
. @senopen
まとめを更新。cshの問題が解決。 これにより,Androidでもcshでも,文字エンコーディングにBOMがあっても動作する,より可搬性の高いシェルスクリプトの記述方法がみつかった。「シェルスクリプトにシバン(#!/bin/s.. togetter.com/li/1077808
発端

会社のNASサーバーでデータ処理をするためにシェルスクリプトを書いて試していたら,以下のPATH変数の初期化で getconf コマンドが見つからないというエラーが出ていた。

export LC_ALL='C' PATH="$(command -p getconf PATH):$PATH"
. @senopen
会社のサーバーパソコン。ディストリビューション不明なのだけど、なんとgetconfがなかった。PATH変数の初期化でスクリプトが落ちていた。 えー。getconfないとかいったいどうしたらいいんだ…
. @senopen
@senopen unamw -aするとarmとかsmpとか表示された。なんだろう?こんなディストリビューションあるの?帰宅したら調べてみよう。
リッチー大佐の中の人 @col_richie
@senopen /etcの中にissueとかos-releaseとかというファイルがあったりしないだろうか。それからarmやsmpとはCPU構成のことではないだろうか。ARMのマルチコアとか……。
. @senopen
@col_richie os-releaseはなく、issueは以下の内容でした。 welcome to TS-231+, QNAP Systems, Inc。なんかベンダー独自ディストリビューションなのでしょうか。始めてみました。
リッチー大佐の中の人 @col_richie
@senopen 製品は qnap.com/ja-jp/product/… で(なるほど確かにarm smp)、OSは qnap.com/event/qts/jp/?… か……。 他にもPOSIX非準拠な点が多数あれば、そもそも準拠する気がない製品として無視してもよいかも。
リッチー大佐の中の人 @col_richie
@senopen もしかしてコマンドやシェルがBusyBox(コマンドが全て1つの実行ファイルへのハードリンク)になっていないだろうか。もしそうだったらさすがにPOSIX原理主義でも救いきれない。
. @senopen
@col_richie busybox使ってました。findとかreadlinkとかgrep, awk, sedなどなど。shはbashでした。busyboxはあまりPOSIX準拠してないのでしたっけ?調べないとわかりませんね…
リッチー大佐の中の人 @col_richie
@senopen あぁやっぱり。BusyBoxは、UNIXであることよりも、貧弱な組み込み環境でも必要最低限のことができることを優先しているので容赦なくPOSIX仕様を満たさないからなぁ。 BusyBoxで使えるコマンドはこちら→ busybox.net/downloads/Busy…
. @senopen
@col_richie げ!なんと!うーん。困りましたね… busybox採用している環境はそれなりにありそうなので、何のコマンドがどう準拠していないのか、把握しておいたほうがいい気がしますね…
リッチー大佐の中の人 @col_richie
@senopen BusyBoxの大半はPOSIXのサブセットなのでPOSIX原理主義のサブセット的な主義を立ち上げるという考えもあれど、その制約を緩和するためにGNU coreutilsコマンドの一部が入ってもいるので、結局BusyBoxはBusyBoxで独自の主義が要るかと…
. @senopen
@col_richie 独自の主義は不要と考えてます。 1. POSIXの定義が全てが常に有効というわけでもない 2. POSIX定義コマンドの全代替実装を把握したわけではない POSIXの乖離性がどれほどあるかわかりませんが,手間でなければBusyBoxをカバーしてもいいかなと
getconfが存在しない場合のPATH変数の初期化方法

実際にgetconfが存在しない環境がみつかった。サーバーなどパッケージングが厳選されているディストリビューションではgetconfコマンドが存在しないことはありえる。
こうした環境でもエラーが出ないようにコードの工夫を試みた。

. @senopen
ええーい。getconfがない場合でもうまくケアしたPATH変数の初期化方法はないものか。 なんとかうまくできないだろうか。あまり長ったらしくなく,やる方法だ。 こんな感じでどうだろうか? command -p $(command -v getconf || :) PATH
zsh対応

getconfが存在しない環境をケアした初期化方法は思いついたが,zshで動作しないという問題が生じた。

. @senopen
間違えた。こうやったら一応動く。 command -p $(command -v getconf || echo :) PATH でもzshだとcommand -p :がnot found。意味不明。
. @senopen
command -pって組み込みコマンドはダメなんだったっけ? -pオプション無しでもzshだとだめだ。 あれか。zshはPOSIX互換のオプションを有効にしないとアカン感じか。man zshmiscのcommandのヘルプ見たらexternal commandって書いてあった。
. @senopen
どうする?/bin/shの実体がzshなことってありえるのか?考慮すべきなのか?考慮する場合は,:をtrueに変えれば事足りるけど。
. @senopen
zshを考慮して完璧なPATH変数の初期化をやるなら。こうなる。 export PATH="$(command -p $((command -v getconf || echo true) | sed 's@.*/@@') PATH):$PATH" 1行に入りきらんぞ…どうしよう
. @senopen
command -pで絶対パスを指定してしまうと,そのパス指定が優先されてしまう。Solarisで結果が変わる。コマンド名だけだと,/usr/xpg6/binをみるけど,command -vだと/usr/xpg4/binをみにいってしまう。だから最後にsedで/を削除した。
残りを読む(206)

コメント

Tsuyoshi CHO @tsuyoshi_cho 2017年2月5日
AndroidとRPiを比較して、OS入れ替えられるから汎用といいいってしまうと、各種自前ドライバを組み込んで活用できるようにがんばってるAndroidをちょっと無視しすぎてるかと思う。OSが動くことと、環境がオープン/ベースラインがあってOS入れ替えて不便しないの差は大きいので(というか組込みLinuxな環境なら苦労すればOSは替える、まではいけるだろうから)
. @senopen 2017年2月7日
まとめを更新しました。 cshの対話シェルから実行すると,shではなくcshで実行されてしまうので,やっぱりシバンは必要という結論になりました。お騒がせしました。
. @senopen 2017年2月21日
まとめを更新しました。 せっかくなのでその後のやりとりについて記録。
. @senopen 2017年2月25日
まとめを更新しました。 シバンがない場合,cshの対話シェルから起動されたら,shで再起動するにして,対応できたので,その話とサンプルコードを掲載。
. @senopen 2017年3月30日
まとめを更新。cshの問題が解決。 これにより,Androidでもcshでも,文字エンコーディングにBOMがあっても動作する,より可搬性の高いシェルスクリプトの記述方法がみつかった。
ログインして広告を非表示にする
ログインして広告を非表示にする