from electron 2 web

インターネットのリソースを無駄遣いして検索におけるUXを下げてごめんなさい

圧倒的文章力のNASAでゴミみたいなチラ裏のようなメモを量産してしまい全ての"Web開発者"にごめんなさい

linuxの/getopts?/について

最近shell scriptをいじりまくってる。やっと少しできるようになった。

getoptsはlinux内部で使われることも多いのでしっかり覚えておくこと。

getoptsには2種類の用法がある。というか普通の使い方はほどほどしないはず。

が、自分は基礎(内部)から理解しないと気が済まないため、がんばって普通のgetoptsにいろいろパターンを変えてブラックボックステストしまくってなんかわかってきた気がするので書いておく。

普通の使い方

forとかwhileで回す。

while getopts :f:r:hd OPT;do
case $OPT in
f) echo "f was setted $OPTARGS"
;;
r) echo "r is reverse"
;;
h) echo "h is help. not haskell"
esac

done

的な。こいつについてももう少し追求したいとこだけど今はとりあえず置いておく。

whileとかforに特別な何かがあるのか?一応getoptsの返り値は0,1なんだが。。。

本題

getopts "a" OPT

するとそのscriptの引数にaが設定された場合、(-a)、$optにaが入っていた。

このとき重要なのが、ただのaでは引数にはならない。ということだ。そして当たり前だが-a -bとしても-abとしても同じ動作をする。まあこれはbash側が解釈するので当たり前なんだが。

また、""を外して,getopts a OPTとした場合、何も入らなくなった。

PS:bash opts.shで実行した場合動くようになった。。。どうしてなんだろうか。。。

囲いなしでbash opts.shすると動くがsource optsすると結果は?になる。これは何も入ってないときの挙動。

""でbash opts.sh -abcとしたとき、source opts.shとしたときはどちらも正常に動く。

  echo 引数=$1
  getopts "abj" opts
  echo getopts結果=$opts

複数の引数をとるとき

getoptsは引数をとると最初のやつしかとらず、2つ目をとるときは2回動かさなければいけないのは前提だが、これは

ただ単にgetoptsの第二引数を参照するごとにインクリメントされるというわけではない。俺はこれをよくやらかしたので注意。

# 引数にaがあると、optsにaが入る。それ以外だと?が入る。
#通常使用時はダブるクオートで囲まないとすべてエラーになる。
  echo 引数=$1
  getopts "abj" opts
  echo getopts結果=$opts


# **Not** only referlence $opts,$opts is not move pointer.
#echo getoptkekka2=$opts

echo secondgetopts
getopts "abj" opts
echo getoptssecond=$opts

英語がバグってるけど許して。このように参照を変えるには2回同じ引数でgetoptsしなければいけない。

ここで重要なことが一つ。スクリプトの実行の仕方について。

スクリプトの実行について挙動で分けるとgroup1=(bash,sh,./);group2=(source,.),group3=(exec)となる。ここでは詳しく説明しないがsourceで動かすと現在のshellに張り付けた挙動をするじゃん?だから何回もその環境で実行すると結果的にgetoptsを何回も実行したことになるので実行するごとにがだんだん変わってく。

i.e.

[root@localhost helloshell]# source opts.sh -a -b -j
getopts結果=a
[root@localhost helloshell]# source opts.sh -a -b -j
getopts結果=b
[root@localhost helloshell]# source opts.sh -a -b -j
getopts結果=j
[root@localhost helloshell]# bash opts.sh -a -b -j
引数=-a
getopts結果=a
getoptkekka2=a
[root@localhost helloshell]# bash opts.sh -a -b -j
引数=-a
getopts結果=a
getoptkekka2=a
[root@localhost helloshell]# bash opts.sh -a -b -j
引数=-a
getopts結果=a
getoptkekka2=a

terminal(PS)Aでsourceで実行したとするとopts.shの中身を一行ごとにPSAに張り付けてそこの環境で実行する感じ。exitとかはない。例えば1回目の後にecho aaaって打てばopts.shの最後の行でecho aaaってしてるのと同じ。

ンで2、3回目のsource実行も同じPSAで行われるから、変数を保持してるのでこんな風に値が変わる。

でもbashで実行するとbashコマンドを打つごとに新しくプロセスB,C,Dができてそこで動くから4,5,6回目のbashの実行はそれぞれ違う環境になっているためaになっている。

ちなみにvimでctrl+zでプロセス停止して出てきたterminal環境とvimで:shellして出てきたterminal環境は違う環境だった。

第一引数を分離した場合

バグらない。そしてこいつも挙動が変わった。

  echo 引数=$1
  getopts a b j opts
  echo getopts結果=$opts
[root@localhost helloshell]# source opts.sh   -b -j
引数=-b
getopts結果=?
[root@localhost helloshell]# bash opts.sh   -b -j
引数=-b
getopts結果=

となった。sourceの場合第二引数でもないのにかかわらず何かが入っている・・・ん?多分これ前に同じ環境で実行してoptsに?が入ってるからだわこれ。。。

ということで""がなくて第二引数以外には入らない。じゃあ""で囲んで分離したら?

getopts "a b j" opts

[root@localhost helloshell]# source opts.sh   -b -j
引数=-b
getopts結果=?
[root@localhost helloshell]# bash opts.sh  
引数=
getopts結果=?
[root@localhost helloshell]# bash opts.sh  -b -j
引数=-b
getopts結果=b

ええ・・・なにこの挙動は・・・

そしていまさら気づいたんだけどgetoptsをn回以上回すと?が入る。言いにくいのでコードで説明しよう。

これは新しく作った環境。そしてgetoptsの引数にはbとjを取るように設定してある。

[root@localhost helloshell]# source opts.sh -b -j
引数=-b
getopts結果=b
[root@localhost helloshell]# source opts.sh -b -j
引数=-b
getopts結果=j
[root@localhost helloshell]# source opts.sh -b -j
引数=-b
getopts結果=?

つまり引数でとれるarrayの中で最後以上になると?になる。つまり、

1まずgetoptsでとってくる引数を設定し、そいつと実際の引数をマッチさせ、最初にまっちしたものをoptsに格納する。

そして次に実行するときというかoptsに何か入っていた場合はそいつを仮想引数から除外してまたマッチさせる。

これを繰り返していくと仮想引数が0になるのでこれはつまり実際の引数と仮想引数がなにもマッチしていないのと同じということ。

まあソース見たほうが早そう(n回目)

bashで実行した場合は"a b c"としても"abc"としてもかわらんっぽい。そしてこれはsourceでも同じだろう。

まあだいぶわかったしこの辺で終わっとく。

conclusion

getoptソース

https://gnunet.org/svn/flightrecorder/src/flightrecorderd/getopt.c

引数の全般的な概要↓良記事。

http://shellscript.sunone.me/parameter.html#%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3%E8%A7%A3%E6%9E%90%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89-getopts-%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B

↓最高

language-and-engineering.hatenablog.jp