- GIDEON
- GIDEON STAFF BLOG
ファイルのオーバーライド
通常のパイプ動作するフィルタコマンドでは
元ファイルを直接オーバライドできない。
例えば sort (1) の -o オプションの様に
元ファイルのオーバライド指定ができる
(この動作をスポンジ動作というらしい)もの以外での処理について。
一番簡単で誰でも思いつくのが一時ファイルを利用するやりかた。
$ command < foo.dat > foo.new $ mv foo.new foo.datこの方法だと command の実行中に割込が発生したりすると 一時ファイルが残ってしまい美しくない。
$ cat foo.dat | ( sleep 1; command > foo.dat)この方法だと sleep (1) の時間がうっとおしく、 データ量や command の速度などによって失敗する場合もありうる。
$ (rm foo.dat; command > foo.dat) < foo.dat若干判りづらいのだがこれで確実に成功する。
先行する rm (1) は必須である。 rm (1) がない場合は外側のシェルが '<' により O_RDONLY で open (2) するファイルと 内側のシェルが '>' により O_RDONLY|O_TRUNC で open (2) するファイルの inode が等しいので、 OS は同一ファイルと見做し command の標準入力は O_TRUC により truncate されたファイルとなってしまう。
対して rm (1) がある場合は、 外側のシェルが O_RDONLY で open (2) するファイルと、 内側のシェルが O_RDONLY|O_TRUNC で open (2) するファイルの inode が異なるので、 OS はそれぞれを違うファイルと見なすためである。
htaccess で任意のファイルのみパスワードを要求しない
Web コンテンツを保護するために
.htaccess ファイルを用いて Basic 認証などを実施する場合は多い。
通常 .htaccess はディレクトリ単位で有効になってしまうのだが、
あるディレクトリのアクセスを Basic 認証でアクセス制御したいが、
特定のファイルについては認証を要求しない設定を行いたい場合は
Files ディレクティブとSatisfy ディレクティブで実現できる。
AuthType Basic AuthUserFile 認証用ファイル AuthName "Enter password" Require valid-user <Files "認証要求しないファイル"> Satisfy Any Allow from all </Files>
Satisfy ディレクティブは Allow ディレクティブと
Require ディレクティブがどちらも使われている場合の
アクセスポリシーを制御する。
Any が指定された場合は Allow か Require
のどちらかの条件を満たせばアクセスが許可されるので、
Files ディレクティブで指定したファイルに対しては
Allow 指定が有効になり認証なしでアクセスが許可される。
POSIX.2 標準仕様のシェルによる変数展開
シェルスクリプトを作成する際に
シェル変数に値を代入したり参照したりする事は頻繁に発生するが、
シェル変数の展開にも便利な使い方がある。
basename (1) や dirname (1) と同様な動作が
シェルの組込みとして利用できるので資源の節約にもつながり、
上手に利用すると可読性の高いスクリプトが作成できる。
機能一覧
${parameter:-word} | デフォルト値への置換 |
${parameter:=word} | デフォルト値の代入 |
${parameter:?[word]} | 値の検査とエラー |
${parameter:+word} | 代替値の使用 |
${#parameter} | 文字列長の取得 |
${parameter%word} | 最短後置パターンの削除 |
${parameter%%word} | 最長後置パターンの削除 |
${parameter#word} | 最短前置パターンの削除 |
${parameter##word} | 最長前置パターンの削除 |
デフォルト値への置換
- ${parameter:-word}
${parameter} が NULL の場合 word に置換される。
$ echo ${foo} $ echo ${foo:-FOO} FOO $ echo ${foo} $ foo=BAR $ echo ${foo:-FOO} BAR
デフォルト値の代入
- ${parameter:=word}
${parameter} が NULL の場合 word に置換され、 かつ parameter に代入される。
$ echo ${foo} $ echo ${foo:=FOO} FOO $ echo ${foo} FOO $ echo ${foo:=BAR} FOO
値の検査とエラー
- ${parameter:?[word]}
${parameter} が NULL の場合 word が指定されていればその値を、 指定されていない場合はデフォルトの値を表示し、 非対話実行されているシェルをエラー終了させる。
$ echo ${foo} $ echo ${foo:?value not set} value not set
代替値の使用
- ${parameter:+word}
${parameter} が NULL 以外の場合 word に置換される
$ echo ${foo:+FOO} $ echo ${foo} $ foo=BAR $ echo ${foo:-FOO} FOO $ echo ${foo} BAR
文字列長の取得
- ${#parameter}
${parameter} の文字列としての長さに置換される
$ echo ${foo} $ echo ${#foo} 0 $ foo=FOO $ echo ${foo} FOO $ echo ${#foo} 3
最短後置パターンの削除
- ${parameter%word}
${parameter} の右から word で示されるパターンの最短部分を削除する
$ bar=/foo/bar/baz $ echo ${foo%/*} /foo/bar $ foo=foo.c $ echo ${foo%.*} foo $ foo=foo $ echo ${foo%.*} foo
最長後置パターンの削除
- ${parameter%%word}
${parameter} の右から word で示されるパターンの最長部分を削除する
$ foo=foo.example.com $ echo ${foo%%.*} foo $ foo=http://www.example.com:8888/ $ echo ${foo%%:*} http $ foo=foo $ echo ${foo%%.*} foo
最短前置パターンの削除
- ${parameter#word}
${parameter} の左から word で示されるパターンの最短部分を削除する
$ foo=foo.c $ echo ${foo#*.} c $ foo=foo.example.com $ echo ${foo#*.} example.com $ foo=foo $ echo ${foo#*.} foo
最長前置パターンの削除
- ${parameter##word}
${parameter} の左から word で示されるパターンの最長部分を削除する
$ foo=foo.example.com $ echo ${foo##*.} com $ foo=/foo/bar/baz $ echo ${foo##*/} baz $ foo=foo $ echo ${foo##*.} foo
wget でファイル送信
wget (1) でファイルを送信する必要が生じたので、 ブラウザでファイルを送信する際のパケットをキャプチャして 通信内容を調べてみました。
想定しているのは input type='file'、
enctype='multipart/form-data' を指定した
フォームによるファイル送信処理。
通常の http ヘッダ以外に "Content-Type" と
"boundary" を wget (1) の
--header オプションで指定して、
multipart/form-data 形式に整形した送信するファイルの内容を
--post-data オプションで指定するだけで送信できます。
Content-Type は 'multipart/form-data'、
boundary は送信するデータの中に含まれない
任意の文字列を指定すれば問題ない様です。
multipart/form-data 形式は以下の形式に整形します。
--任意の文字列 Content-Disposition: form-data; name="フォーム名"; filename="ファイル名" Content-Type: text/plain (空行) ファイルの中身 (空行) --任意の文字列
実際にコマンドライン形式に記述するとこの様になります。
wget --header="Content-Type: multipart/form-data; boundary=バウンダリ" --post-data=' --バウンダリ Content-Disposition: form-data; name="フォーム名"; filename="ファイル名" Content-Type: text/plain ファイルの中身 : --バウンダリ ' 送信先URL
スクリプトの場合などはシェル変数などを利用すると、 もう少し見やすいスクリプトになるでしょう。
#!/bin/sh : : # バウンダリ文字列は世界標準時基準時点からの秒数とする boundary="boundary-`date '+%s'`" # 受信する cgi の <input type="file" name="..."> で指定されるフォーム名 formname="フォーム名" # 送信するファイル filename="ファイル名" # 送信するファイルのデータ contents="`cat ${filename}`" # multipart/form-data 形式のデータ生成 data=" --${boundary} Content-Disposition: form-data; name='${formname}'; filename='${filename}' Content-Type: text/plain ${contents} --${boundary} " # ファイルの送信処理 wget --header="Content-Type: multipart/form-data; boundary=${boundary}" \ --post-data="$data" 送信先 URL : :
USBメモリからブートできてパーティション操作やファイル復元が可能な『Parted Magic』
ext4に対応した Parted Magic 3.5 でパーティションを操作する
こんなディストリビューション(?)あったのか。 知らなかった。 USBメモリからブートできるイメージがあるのはかなりありがたい。 いざというとき存在を知ってれば便利そうだ。
主要なコンピュータ言語/コンパイラのベンチマーク
スクリプト言語のベンチマークを探していて辿りついたページ。
The Computer Language Benchmarks Game
Rubyは1.9になって結構速くなってるとか、Python3は2.Xと比べてちょっと遅くなってるとか見てるだけで面白い。
なお、最初に選択するOS and CPUによって、選択できる言語が変わる。
GNU Bourne-Again Shell を便利に使う
対話シェルとして使用する bash (1) の
コマンドライン履歴機能の便利な使い方。
bash (1)のコマンドライン履歴機能のうち
イベント指示子、単語指示子、修飾指示子は
オンラインマニュアルにも情報が少ないが、
慣れると非常に便利でキータイプ量も減るので是非マスターしておきたい。
以下の文中で n、m は任意の整数、
foo、bar、baz などは任意の文字列を示します。
機能一覧
イベント指示子 | |
---|---|
!! | 直前に実行したコマンドを実行する |
!-n | n 回前に実行したコマンドを実行する |
^foo^bar !:s/foo/bar |
直前に実行したコマンドの foo を bar に変更して実行する |
!:gs/foo/bar | 直前に実行したコマンドの全ての foo を bar に変更して実行する |
!foo | foo で開始されるコマンドを実行する |
!n |
n 番目に実行されたコマンドを実行する |
単語指示子 | |
!:n | 直前に実行したコマンドの n 番目の引数を取得する |
!^ | 直前に実行したコマンドの 1 番目の引数を取得する |
!$ | 直前に実行したコマンドの最後の引数を取得する |
!* | 直前に実行したコマンドの全ての引数を取得する |
!:n* | 直前に実行したコマンドの n 番目から最後までの引数を取得する |
!:n-m | 直前に実行したコマンドの n 番目から m 番目までの引数を取得する |
!:-n | 直前に実行したコマンドの最初から n 番目までの引数を取得する |
!:n- | 直前に実行したコマンドの n 番目から最後の 1 個前までの引数を取得する |
修飾指示子 | |
:p | コマンドを表示のみして実行しない |
:h | パスのファイル名部分を取り除きディレクトリ名を取得する |
:t | パスのディレクトリ名部分を取り除きファイル名を取得する |
:r | ファイル名の拡張子を取り除き "." の前を取得する |
:e | ファイル名の拡張子を "." 付きで取得する |
イベント指示子
- !! 直前に実行したコマンドを実行する
$ echo foo bar baz foo bar baz $ !! echo foo bar baz foo bar baz
$ echo foo bar baz foo bar baz $ echo FOO BAR BAZ FOO BAR BAZ $ !-2 echo foo bar baz foo bar baz $ !-2 echo FOO BAR BAZ FOO BAR BAZ
!:s/foo/bar
$ echo foo foo foo foo foo foo $ ^foo^bar echo bar foo foo bar foo foo $ echo foo foo foo foo foo foo $ !:s/foo/bar echo bar foo foo bar foo foo
$ echo foo foo foo foo foo foo $ !:gs/foo/bar echo bar bar bar bar bar bar
$ echo foo bar baz foo bar baz $ touch foo bar baz $ !ec echo foo bar baz foo bar baz
$ echo foo bar baz foo bar baz $ echo FOO BAR BAZ FOO BAR BAZ $ !1 echo foo bar baz foo bar baz
単語指示子
- !:n 直前に実行したコマンドの n 番目の引数を取得する
$ echo foo bar baz foo bar baz $ echo !:0 echo echo echo $ echo foo bar baz foo bar baz $ echo !:2 echo bar bar
$ echo foo bar baz foo bar baz $ echo !^ echo foo foo
$ echo foo bar baz foo bar baz $ echo !$ echo baz foo
$ echo foo bar baz foo bar baz $ echo !* echo foo bar baz foo bar baz
$ echo foo bar baz foo bar baz $ echo !:2* echo bar baz bar baz
$ echo foo bar baz FOO BAR BAZ foo bar baz FOO BAR BAZ $ echo !:3-5 echo baz FOO BAR baz FOO BAR
$ echo foo bar baz FOO BAR BAZ foo bar baz FOO BAR BAZ $ echo !:-4 echo foo bar baz FOO foo bar baz FOO
$ echo foo bar baz foo bar baz $ echo !:1- echo foo bar foo bar
修飾指示子
- :p コマンドを表示のみして実行しない
$ echo foo bar baz foo bar baz $ touch foo bar baz $ !e:p echo foo bar baz
$ echo /foo/bar/baz /foo/bar/baz $ echo !$:h echo /foo/bar /foo/bar
$ echo /foo/bar/baz /foo/bar/baz $ echo !$:t echo baz baz
$ echo foo.c foo.c $ echo !$:r echo foo foo
$ echo foo.c foo.c $ echo !$:e echo .c .c
迷惑メール防止法本日より施行
今回の改正はオプトイン方式( 受信を同意した相手にしか特定の電子メールを送信できなくなる) へ移行した事が一番大きなポイントであるが、 他にも厳罰化や送信者情報偽装の禁止などが盛り込まれている。
php からのディスクアクセス
php からディレクトリにある全ファイルの一覧を取得する処理のパフォーマンスを exec()、popen()、opendir() を利用した処理で比較してみる。
方法は単純にテスト用のディレクトリに 500 ファイルを用意して、 それぞれの関数を使用してファイル名一覧を取得する処理を 10回実行した場合の処理速度を計測する。
exec("ls -1") | 0.774762 |
popen("ls -1") | 0.865914 |
opendir() | 0.136642 |
外部 shell を実行しないので opendir() が早いだろうとは予想していたが、
これほど差が出るとは思わなかった。
パフォーマンスを優先する場合は勿論だが、
余計なプロセスを生成しないので opendir() が地球に優しい様だ。
<? $func = array("byls", "bypopen", "byopendir"); for($i=0; $i<3; $i++){ $dist = array(); $start[$i] = gett(); for($j=0; $j<100; $j++) $dist[$j] = $func[$i]("/tmp/test"); $end[$i] = gett(); printf("%-10s:%f\n", $func[$i], $end[$i] - $start[$i]); } function byls($dir) { exec("ls -1 $dir", $output); return($output); } function bypopen($dir) { if(($fp = popen("ls -1 $dir", "r"))){ while($buf = fgets($fp)) $output[] = trim($buf); pclose($fp); } return($output); } function byopendir($dir) { if($dir = dir($dir)){ while($file = $dir->read()) $outout[] = trim($file); $dir->close(); } } function gett() { $t = gettimeofday(); return((float)($t['sec'] + $t['usec'] / 1000000.0)); } ?>
ついでにシェルの glob なパターン指定を利用して 指定した文字列にマッチするファイル名の一覧取得を行う場合のテストもしてみた。
20071203000 から 20071203499 までの500個のファイルの中から、 200712032?? にマッチするファイル名の一覧取得で時間を計測してみる。
exec("ls -1 200712032??") | 0.883770 |
popen("ls -1 200712032??") | 0.902392 |
opendir() -> preg_match("/200712032../") | 0.188792 |
glob("/200712032../") | 0.203928 |
exec()、popen() が遅いのは相変わらずだが、
preg_match() の呼出しが意外に早かったのが驚き。
正規表現のマッチ処理って基本的に相当時間がかかる筈なんだが…
glob() はファイルシステムに自分でアクセスしてる様なので早い。
複雑なパターン指定が必要なら preg_match() の方が柔軟だが、
シェルに渡せる程度のパターン指定なら glob() を利用するのが
可読性やメンテナンス性と性能が両立できて良いのかも?