それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

シェルのshebang が実行されるときの引数を見てみる。

linux でコマンドを実行する shebang

shebang を指定すると、そのコマンドでプログラムが実行されますよね。

#!/usr/bin/env ruby
puts 1 

このとき、shebangの1行目に指定したコマンドにはどのように引数が渡されているのだろうか。 ちょっと試してみよう。

shebangで実行できるもの

shebang に指定できるものは、実行可能な「バイナリ」だけ。*1
shebang に指定した shebang を使ったファイルは無効になる。

そこで、バイナリを用意する

引数を表示するsample.c

#include <stdio.h>


int main ( int argc, char** argv ){

  for(  int idx=0; idx<argc; idx++){
    printf("argv[%d]= %s \n" , idx, argv[idx]);
  }
}

これをコンパイルして実行可能なプログラムを作る

gcc sample.c -o sample

shebang に指定

今作ったプログラムをshebang に指定する。

shebang-test

#!./sample

This is test. 
This body will not be executed 

shebang の書き方がよく見るものと違っていますが、これは相対パスです。フルパスで指定するのが面倒くさいので相対パスで指定しています。

実行してみる

それでは、このファイルに実行可能フラグをくっつけて実行してみましょうか。

takuya@~$ chmod a+x   shebang-test

実行した結果

takuya@~$ ./shebang-test
argv[0]= ./sample
argv[1]= ./shebang-test
  • 引数0に、作ったプログラムが表示されており、
  • 引数1に、作ったファイル名が指定されている。

オプションを付けた場合

オプションを付けてみましょう

takuya@~$ ./shebang-test this is argument from command line
argv[0]= ./sample
argv[1]= ./shebang-test
argv[2]= this
argv[3]= is
argv[4]= argument
argv[5]= from
argv[6]= command
argv[7]= line

ファイル名の後ろに追加されたのがわかります。

shebang 行にオプションを付けてみましょう

では、shebang行にオプションを付けてみます。

#!./sample from-shebang arguments 1 2 3

this is test

これで実行してみます。

shebang 行にオプションを付けた場合

takuya@~$ ./shebang-test
argv[0]= ./sample
argv[1]= from-shebang
argv[2]= arguments
argv[3]= 1
argv[4]= 2
argv[5]= 3
argv[6]= ./shebang-test

すると、コマンドー引数ーファイル名の順番になっていることがわかります。

両方に引数を付けてみる。

最後に両方にオプションを付けてみる。

takuya@~$ ./shebang-test a bc
argv[0]= ./sample
argv[1]= from-shebang
argv[2]= arguments
argv[3]= 1
argv[4]= 2
argv[5]= 3
argv[6]= ./shebang-test
argv[7]= a
argv[8]= bc

今度は、コマンド-引数ーファイル名ー引数になっていることがわかります。

shebang が実行されるまで

シェル(sh/bash)はexecv でコマンドを実行する。execv がコマンドを実行するときに、 shebang があればインタプリタだとして1行目を処理してくれる。

execv を直接コールすれば少しわかる気がする

execv-test-01.c

execvを使って最初に作ったコマンド sample を実行してみます。

#include <stdio.h>
#include <unistd.h>

int main (){
  char* argv[2] = {"aaaaaaaaa", NULL };
  execv("./sample", argv );
}

実行結果

takuya@~$ gcc  exec-test-01.c && ./a.out
argv[0]= aaaaaaaaa

shebangファイルをexecvで実行

shebang ファイルをexecv で実行してみると。

exec-test-02.c

#include <stdio.h>
#include <unistd.h>

int main (){

  char* argv[2] = {"aaaaaaaaa", NULL };
  execv("./shebang-test", argv );

}

これを実行すると

takuya@~$ gcc  exec-test02.c && ./a.out
argv[0]= ./sample
argv[1]= ./shebang-test

引数 aaaaaa を与えたはずだけど消えてしまった。ということは 最初の引数を重ね合わせはシェルの機能だったぽい (実験環境がmacOSで試したからこうなったという可能性もあるが、unistdがそんなにきょどうかわる??)

shebang の重ね合わせ

Since Linux 2.6.28, the kernel permits the interpreter of a script to itself be a script. This permission is recursive, up to a limit of four recursions, so that the interpreter may be a script which is interpreted by a script, and so on.

Linuxの最新版に限り、Shebangの重ね合わせが可能になっている。

参考資料

http://man7.org/linux/man-pages/man3/exec.3.html

広告

*1: 後述しますが、一般的なUNIXはそうなってる。Linux 2.68 以降はそうでもない。