Bash基礎文法最速マスター。これはひどい。この手のシェル入門者向けまとめはこれまで何度か見てきたけど、その中でもダントツにひどい。何がひどいって、必要なことがことごとく抜け落ちていて、にもかかわらず「基礎」には必要のないことばかり書かれているということ。_ 冒頭からおかしい。
他の言語をある程度知っている人はこれを読めばBashの基礎をマスターしてBashを書くことができるようになっています。無理。そもそも、他の言語と同じように書こうという発想そのものが間違っている。_ シェルスクリプトというのは各種コマンドをうまい具合に組み合わせて目的を達するために書くものであって、それ単体で複雑な処理やらアルゴリズムを記述するものではない。そういうことが必要ならば、クソな言語仕様の sh なんか捨ててもっとマシなスクリプト言語を使うべきだ。昔ながらの sh では配列が使えないけど bash なら配列が使える。だから bash を使おう、ではなく、配列が必要なほどのデータを管理しなければならないのならばはじめから perl なり ruby なりの言語を使う、あるいは sh でやるにしても自分自身でデータを管理するのではなく外部コマンドのパイプラインにうまく載せて最終結果だけを受け取るように書く。bash の配列を駆使してクイックソートを書いても誰も褒めない。アホがいる、と笑われるだけ。
_ 最低限必要なのは、変数やワイルドカードの展開、パイプラインやリダイレクト、正常終了がゼロでエラーはそれ以外というルール(条件判断の [ ] はカッコではなくコマンドであるという事実)。それにまったく触れないで基礎文法も何もあったもんじゃない。
_ ただの Hello, world です。まだやってんのか。
#!/bin/sh ____=.... _____=..... __=$____$_____ _______=/???/???/??${#_____}??? [ "$_______" = $_______ ] && _______=/????/[^${#_________}-${#__}][^${#_________}-${#__}]${#_____} __=`$_______<<_` __=${__#?????} ________=${__#?} ________=${__%$________} __=${__#???????} ______=${__#?} ______=${__%$______} __=${__#????} ___=${__#?} ___=${__%$___} ______=`/$______??/$___$________?? /$______??/$___$________??` ___________=${______#*$___$________?} __=\\${#_________} _______=$__${#___}${#_____}${#____}$___________ [ "_`$______ -$___ $_______`" = "_-$___ $_______" ] && ______=${______#/???/} || [ "_`$______ $_______`" = "_$_______" ] && ______="$______ -$___" __________=$__${#___}${#_______}${#__} _______=$_______, $______ $__${#___}${#___}${#_________}$___$__${#___}${#_____}${#____}$_______ $__${#___}${#__}${#_______}$___________$__________$__${#___}${#_____}${#____}$__${#___}${#____}${#____}!
- スクリプト名の縛りや ホームディレクトリ位置の縛りはありません
- 動くことを確認
- FreeBSD + { ash, ksh }
- Debian + { dash, bash }
- OpenSolaris + { ksh, bash }
- MacOSX + bash
- 動かないことを確認: zsh
- 確認してないけど動かないはず: OpenSolaris じゃない Solaris、元祖 Bourne sh
- 既知の不具合: ksh -x で実行すると core を吐いて落ちる(-x がなければ問題ない)
_ echo の4文字を得るために、これまでは $0 (スクリプト名)や ~ (ホームディレクトリ)からスタートしていたけど、これらは環境に激しく依存する。記号だけでアルファベットを作るのは困難だけど、数字は ${#var} で簡単に作れるので、数字を含むコマンドをワイルドカードにマッチさせることを考えた。
_ いろんな環境にあって数字を含むコマンドといえば /usr/bin/m4 だけど、記号しか使えなければ m4 では cat 相当のことしかできない。うーんどうしたもんかと半日悩んだ末に md5 を取ることにした。しかし、これ環境に依存する。なんとかがんばって記号文字だけで /usr/bin/md5sum (linux, opensolaris) と /sbin/md5 (freebsd, macosx) を判別させることに成功したが、この時点で zsh (意図しないタイミングでワイルドカードが展開される)と solaris (/usr/bin/md5sum や /sbin/md5 じゃなくて digest -a md5 を使え)が脱落。
_ ここまでできればあとは空文字列の md5 から b,c,e を拾って /b??/ec?? にマッチさせて /bin/echo を作り、 これまでと同じように8進文字コードを echo して完了。せっかくなので各種環境の echo の仕様の違い(シェル内蔵 echo と /bin/echo のどちらを使うか、-e は必要かどうか)も判別させるようにして、かなり環境非依存な Hello, world になった。
_ 以下、アンスコな変数名に置換する前のものにコメントを付加したもの。
s4=.... s5=..... s9=$s4$s5 md5=/???/???/??${#s5}??? [ "$md5" = $md5 ] && md5=/????/[^${#s0}-${#s9}][^${#s0}-${#s9}]${#s5} # /usr/bin/md5sum or /sbin/md5 # /????/??5 でなく /????/[^0-9][^0-9]5 なのは /proc/345 にマッチさせないため sum=`$md5<<_` # 空文字列の md5 (d41d8cd98f00b204e9800998ecf8427e) sum=${sum#?????} # b,c,e を切り出す c=${sum#?} c=${sum%$c} sum=${sum#???????} b=${sum#?} b=${sum%$b} sum=${sum#????} e=${sum#?} e=${sum%$e} echo=`/$b??/$e$c?? /$b??/$e$c??` # /bin/echo をゲット! o=${echo#*$e$c?} # /bin/echo から o を切り出す x=\\${#s0} lo=$x${#e}${#s5}${#s4}$o # 8進文字列を作る (\0154o -> "lo") [ "_`$echo -$e $lo`" = "_-$e $lo" ] && echo=${echo#/???/} || [ "_`$echo $lo`" = "_$lo" ] && echo="$echo -$e" # 各種環境の echo の仕様に合わせて {/bin/,}echo{, -e} のどれを使うか判定 r=$x${#e}${#lo}${#x} # \0162 -> "r" lo=$lo, $echo $x${#e}${#e}${#s0}$e$x${#e}${#s5}${#s4}$lo $x${#e}${#x}${#lo}$o$r$x${#e}${#s5}${#s4}$x${#e}${#s4}${#s4}! # \0110e\0154\0154o, \0127o\0162\0154\0144! -> "Hello, World!"
_ うーん、なんだかなー。
_ FTPS や SFTP ではない素の FTP を使うのが間違い、とかいってる人もいるみたいだけど、それ違うでしょ。いや、そりゃ暗号化できればそれに越したことはないけど、今回の FFFTP 騒動は通信内容を傍受されたわけではなくて、ローカルに置いてあった情報が盗まれる、という話。FTP じゃなくてFTPS を使っていたとしても、その FTPS をおこなうアプリケーションがログイン情報を平文ないしは簡単に解読できる形式でローカルに保存していたら同じように盗まれてサイトを改竄される可能性がある。つまり純粋にアプリケーションの実装ないしは利用者の運用の問題であって、プロトコルの問題ではない。
_ 今回はたまたま FFFTP やその他の FTP クライアントがターゲットになったけど、プロトコルの問題ではないので、同じような攻撃はほかでも起きうる。どっかの Web サービスが HTTPS で運営されていたとしても、そのログイン情報や Cookie がローカルに平文で保存されていたら、それを盗まれて勝手に自分のアカウントでなんか変なことをされるかもしれない。メールサーバが TLS 対応だったとしても、ログイン情報がローカルに平文で保存されていたら、それを盗まれて他人にメールを読まれたり spam 送信に使われるかもしれない。telnet じゃなくて ssh を使っていたとしても、秘密鍵がパスフレーズなしで使えるものだったら、それを盗まれて他人にログインされてしまうかもしれない。ぶっちゃけ、ログイン情報を PC に保存できて、次回からそういう入力が一切不要でログインできてしまうアプリはぜんぶ危険。
_ もちろん、素の FTP よりも FTPS が使えるのならそっちの方がいいのは言うまでもない。ただ、ネットワークトラフィックの盗聴を防ぐことと、ローカルデータを盗まれることを防ぐことはまったく異なる話であって、どちらか片方やればいいという問題ではない、両方やらなきゃダメ、ということは理解しておかなければならない。こういう悪さをする malware に感染しないように日頃から脆弱性対策したりアンチウイルスソフトの定義ファイルアップデートに勤しむのも重要だけど、ゼロデイ攻撃もあるので完全ではない。重要な情報、せめてパスワードぐらいは PC に保存せずに自分の頭で覚えておく、というのがいいんだろうけど、脳味噌の記憶領域はそんなに大きくないよ! どうすんの! どうしようもないよ! うーん、困った(←ヒドいオチだ)。
_ あー、そうだ。malware によるトラフィックの盗聴対策には通信経路の暗号化が有効なんだけど、stunnel のような平文-SSL ゲートウェイなツールは役に立たない、ということは理解しておいた方がいいかな。
_ こういうツールは自分の PC と外部との通信を暗号化するのが目的なので盗聴者が外部にいる場合には有効だけど、同じ PC の中で malware が盗聴してれば SSL 変換前の通信が見えちゃって意味なし。アプリが自前で暗号化するものじゃないとダメ。
_ ただし、トラフィックを覗き見しているのは malware だけでなく、アンチウィルスソフトやアプリケーションファイアウォールも危険な通信がおこなわれていないか監視している。アプリが自前で暗号化している場合この監視もできなくなるので注意。最近のアンチウィルスはこれをムリヤリ監視するために、ネットワークトラフィックではなく、アプリに対するプラグインのような形でアプリ内部で動作するものもあるけど、アプリごとに個別に対応する必要があるので、かなりメジャーなブラウザやメーラーしか対応していない。
_ 0 が消えて記号だけで Hello, world が書けるようになりました。スクリプト名が hello.sh でなければならない、という縛りもなくなりました。ただし、MacOSX でしか動きません。Snow Leopard の /bin/sh (=bash) で確認。OSX でも root だと動きません。
#!/bin/sh _____=~ _____=${_____#/??} ______=${_____#?} _____=${_____%$______} ____=${______#?} ______=${______%$____} __=`/???/$_____??[^$______] /???/$_____??[^$______]` __=${__#/???/} _________=${__#???} ____=\\${#_______}${#_____} _______=$____${#__}${#__} ________=$____${#_______}${#__} ___=$________$_______ _______=-$_____ __________=$________$_______ $__ $_______ $____${#___}$_____$________$________$_________, $____${#_______}${#__________}$_________$______$___!_ 解説。単純に echo Hello, World! してるだけ。具体的な手順は次のとおり。~ (自分自身のホームディレクトリ、OSX では /Users/XXX) から e と r の文字を切り出す。ワイルドカードで /???/e??[^r] をマッチさせて /bin/echo を得る([^r] は /bin/expr とマッチしないようにするため)。得られた /bin/echo から echo を得る(BSD 系の /bin/echo は -e が使えないためシェルのビルトイン echo を使う)。あとは変数 $var の文字列長を ${#var} で得られることを利用して8進数で文字コードを作り、echo -e で出力。
_ というわけで、ホームディレクトリが /Users/XXX の OSX ではなく /home/XXX や /export/home/XXX な環境ならば、~ から h、o、e を取り出して /???/e?ho にマッチさせて echo を取り出す、という手順に書き換えればよい。 0 を含んだバージョンの場合は $0 (= 自分自身のスクリプト名、今回は hello.sh) から h、e、o の文字を切り出し以下同じ、でやっている。
_ とにかくいかにして echo の4文字を得るかがキモ。echo さえできればあとは eval という文字列も作れるので、Hello, world どころかもっと複雑な制御構造を持つスクリプトも書けるようになるはず。ためしてないけど。echo を得るのにワイルドカード展開以上にうまい方法が見つからなかったので、たとえば事前に touch /tmp/echo しておくと、あるいはその他まぎらわしいファイルが存在すると、それだけでマッチに失敗してコケる。
_ ただの Hello, world です。
文字化けじゃありません。> cat hello.sh #!/bin/sh ___=${0%.*} ___=${___##*/} ____=${___#?} ______=${___%$____} ________=${____%???} _______=${____#???} _____=${____%$_______} ___=`/???/$________?$______$_______ /???/$________?$______$_______` ___=${___#/???/} __=\\${#__} ______=${_____#??}$__${#_______}${#____}${#___} _____=$__${#_______}${#______}${#__} ______=$______! $___ -$________ $__${#_______}${#________}${#_________}$____, $__${#________}${#__}${#______}$_______$_____$______ > sh hello.sh Hello, World!1個だけある 0 を消すうまい方法をだれか考えてください。
- スクリプト名は hello.sh じゃないと動きません
- bash、ash (freebsd)、zsh で動作確認
- solaris の ksh はダメだけど freebsd ports の ksh では動く(SysV と BSD の違いか)
- 最下行先頭の $___ を /???/$___ に変更すると dash (debian) で動くようになるが freebsd, macosx で動かなくなる