8565


シェルスクリプトをコマンドラインからしか実行できないようにしたいです。

そこで思いついたのが、pythonで見たことある以下の書き方です。  
(importでは実行されないようにしている）

[code]

    if __name__ == '__main__':
        pass
    
[/code]

シェル（bashを想定）でもこんな簡単に出来るでしょうか。  
また、そもそも別の方法があればご教示頂きたいです。

特殊変数の$0と実ファイル名を比較する方法だと、実ファイル名を変更した時にスクリプト内のファイル名も修正しないといけない気がしてます。そこで上記のpythonのようなやり方が  
ベストかなと思いついたところです。

よろしくお願いします。


CC BY-SA 3.0
2015-03-28T17:26:31.100
8518
2015-10-15T04:44:15.667
8695
question
python bash sh
シェルスクリプトをコマンドラインからしか実行できないようにしたい


> 別のシェルスクリプトファイルからは実行できない（読み込まれない）ようにしたい

シェル変数[`$SHLVL`](http://www.atmarkit.co.jp/flinux/rensai/linuxtips/529shelldep.html)を使えば、要望に近いことは実現できます。以下は`bar.sh`から`foo.sh`を呼び出す例です。

[code]

    $ cat foo.sh
    #!/bin/sh
    if [ 2 -lt $SHLVL ]; then
      exit
    fi
    echo Hello,
    
    $ cat bar.sh
    #!/bin/sh
    ./foo.sh
    echo World!
    
    $ ./foo.sh
    Hello,
    $ ./bar.sh
    World!
    
[/code]

ただし、新しいシェルを起動せずに`bar.sh`を呼び出したり、シェルレベルが2以上の状態で`foo.sh`を呼び出すケースには対応できません。

[code]

    $ . bar.sh
    Hello,
    World!
    
    $ bash
    $ echo $SHLVL
    2
    $ ./foo.sh
    Hello,
    
[/code]


CC BY-SA 3.0
2015-03-30T05:01:04.300
8552
2015-03-30T05:01:04.300
49
8518
answer


bashのバージョンが3以上であれば、`BASH_SOURCE`が使えると思います:

[code]

    #!/bin/bash
    [ "${0}" = "${BASH_SOURCE[0]}" ] || return 0
    
[/code]

冒頭に入れておけばスクリプトが`source`された時にreturnします。

* * *

追記: 他のスクリプトの中で実行されるパターンを潰すのであれば、親プロセスから実行コマンドを確認するのがスマートかなぁと思います。

[code]

    #!/bin/sh
    [ "`ps -o comm= $PPID`" = "-bash" ] || exit 1
    
[/code]

bashの前についているハイフン`-`はログインシェルを意味します。

[code]

    $ man bash | less '+/^[ ]*exec '
    
[/code]

> If the -l option is supplied, the shell places a dash at the begin-ning of  
>  the zeroth arg passed to command. This is what login(1) does.


CC BY-SA 3.0
2015-03-30T10:34:55.593
8565
2015-10-15T04:44:15.667
2015-10-15T04:44:15.667
62
62
8518
answer


`caller`を使って呼び出し元をチェックできます。`sh`でも動くと思います。

[code]

    #! /bin/sh
    
    function called_by() {
        local by
        by=$(caller 0)
        by=${by#* }
        by=${by% *}
        echo -n "$by"
    }
    
    if test "$(called_by)" = source
    then
        echo "Do not source"
    fi 1>&2
    
[/code]


CC BY-SA 3.0
2015-10-14T06:50:44.650
17633
2015-10-14T06:50:44.650
12773
8518
answer


Shebang行を読んだOSにより実行された場合に、特定の環境変数を設定する方法を考えてみました。  
Shebang行を以下のようにすれば起動されたインタプリタのプロセスの環境変数`"__SHEBANG__"`が "1" に設定されます。

[code]

    #!/usr/bin/perl -e$ENV{'__SHEBANG__'}=1;exec('インタプリタ',@ARGV)
    
[/code]

Shellスクリプト部では、この`"__SHEBANG__"`が `"1"`かどうかで処理を分けることが出来ます。

[code]

    #!/usr/bin/perl -e$ENV{'__SHEBANG__'}=1;exec('sh',@ARGV)
    # shell script (not perl)
    
    [ "$__SHEBANG__" = "1" ] || exit 1
    unset __SHEBANG__
    
    echo 'shebang行の設定により起動されました'
    
[/code]

Pythonなどでも同じように出来ます。  

[code]

    #!/usr/bin/perl -e$ENV{'__SHEBANG__'}=1;exec('python3',@ARGV)
    # python3 script (not perl)
    
    import os
    import sys
    
    if os.environ.get('__SHEBANG__', '') != '1': sys.exit(0)
    del os.environ['__SHEBANG__']
    
    print('shebang行の設定により起動されました')
    
[/code]

Shebang行にPerlを使っているのは以下を満たすコマンドがPerlしか思い付かなかったからです。

  1. 環境変数を設定出来る
  2. 与えられた引数をそのまま渡して別プロセスを起動できる
  3. 上記の処理を、空白を含まない一つの引数で指示できる  
(Shebang行の引数部分に空白があった場合、OSによって扱いが違います。  
Linuxの場合、空白を含んだ一つの引数と見なされますが、空白で区切られた複数の引数と見なすOSもあるようです。  
よってshebang行では、空白を含まない一つの引数のみ使うようにしました)


CC BY-SA 3.0
2015-10-14T11:52:14.520
17650
2015-10-14T11:52:14.520
3054
8518
answer