1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

シェバンでスクリプトの直接実行を防ぐトリック: #!/bin/echo の活用

Posted at

はじめに

シェルスクリプトを書いていると、「このスクリプトは直接実行してほしくない」「`source`で読み込んで使ってほしい」という場面に遭遇することがあります。例えば、DockerのPostgreSQLコンテナで使う`initdb.d`内のスクリプトで、直接実行されると意図しない動作をする場合などです。

今回は、シェバン(`#!`)を工夫して、スクリプトの直接実行を防ぎつつ、実行時にメッセージを表示するシンプルな方法を紹介します。使うのは、身近なコマンド`/bin/echo`です。このトリックを知れば、スクリプトの制御がちょっと楽しくなるかもしれません!

問題: スクリプトの直接実行を防ぎたい

例えば、以下のようなスクリプトを考えます。これは`initdb.d`で共通関数を定義するモジュールです:

initdb.d/common.sh
function log() {
    echo "[INFO] $1"
}

このスクリプトは、他のスクリプトから`source`で読み込んで使う想定です。しかし、誰かがうっかり`./common.sh`と実行してしまうと、特に何も起こらないものの、意図しない動作(例えば環境変数の汚染)を引き起こすリスクがあります。さらに、`initdb.d`内で直接実行されると、期待した初期化が動かないことも。

ここで欲しいのは:

  • 直接実行(`./common.sh`)されたら「これは`source`で使ってね」とメッセージを表示。
  • `source`で読み込んだときはエラーなく関数を利用可能。
  • `initdb.d`で余計な実行を防ぐ。

さて、どうしましょうか?

解決策: `#!/bin/echo` を使う

シンプルな解決策として、シェバンに`/bin/echo`を使います。以下がその実装です:

initdb.d/common.sh
#!/bin/echo This script is meant to be sourced, not executed directly.
function log() {
    echo "[INFO] $1"
}

どう動くのか?

  1. 直接実行時(`./common.sh`)
    シェバンが`/bin/echo`を呼び出し、続く文字列を引数として出力します:
    ```
    This script is meant to be sourced, not executed directly.
    ```
    その後、`echo`が終了し、スクリプトの残りの部分(`function log`)は実行されません。

  2. `source`時(`source ./common.sh`)
    シェバンは無視され、スクリプト全体が現在のシェルで解釈されます。2行目以降がBashコードとして処理され、`log`関数が正しく読み込まれます:

    $ source ./common.sh
    $ log "test"
    [INFO] test
    
  3. `initdb.d`での動作
    Dockerの`initdb.d`で実行されると、シェバンにより`/bin/echo`が呼ばれ、メッセージが出力されて終了。関数定義などの余計な処理はスキップされます。

なぜこれでうまくいくのか?

  • `/bin/echo`は引数を出力して即終了する軽量コマンド。
  • シェバンの後ろに書いた文字列がそのまま`echo`の引数になる。
  • Bashではシェバンが無視されるため、`source`時の動作に影響なし。

他の方法との比較

似た目的でよく使われる方法と比べてみましょう:

`/bin/true` + 条件分岐

#!/bin/true
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    echo "This script is meant to be sourced, not executed directly."
    exit 1
fi
function log() {
    echo "[INFO] $1"
}
  • メリット: メッセージをスクリプト内で柔軟に編集可能。
  • デメリット: 少し冗長で、シェバンだけで完結しない。

`/bin/echo` の勝ちポイント

  • 1行で完結するシンプルさ。
  • 外部依存性がほぼゼロ(`/bin/echo`はどこにでもある)。
  • 直感的で「なるほど!」と思えるトリック感。

注意点

この方法を使う際に気をつけるべき点を挙げます:

  • メッセージの編集: シェバンにハードコードされるので、長いメッセージや動的な内容には不向き。
  • 改行: シェバン内で`\n`を入れてもリテラルとして扱われるので、改行は自然に出ません。必要なら`printf`を検討してもいいかも。
  • 可読性: チーム開発では「なぜ`echo`がシェバンに?」と驚かれる可能性があるので、コメントで意図を補足すると親切です。

応用例: Dockerでの利用

`initdb.d`で使う場合、こんな感じで実装できます:

initdb.d/common.sh
#!/bin/echo This script is meant to be sourced, not executed directly.
function log() {
    echo "[INFO] $1"
}
initdb.d/01-init.sh
#!/bin/bash
source /docker-entrypoint-initdb.d/common.sh
log "Starting database initialization"
psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "CREATE TABLE test (id SERIAL PRIMARY KEY);"

直接`common.sh`が実行されてもメッセージで警告しつつ、`01-init.sh`では関数を活用できます。

おわりに

`#!/bin/echo`を使ったこのトリックは、シェルスクリプトの小さな工夫として意外と便利です。シンプルながら特定の場面(特にDocker環境)で役立つ場面があるので、ぜひ試してみてください。もし他にも面白いシェバンの使い方を見つけたら、コメントで教えてくださいね!

1
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?