リダイレクトの基礎復習

今までなんとなくつかっていたので、あらためて、基礎復讐。

標準入出力とファイルディスクリプタ

ファイルディスクリプタ

プロセスがなんらかのファイルやディレクトリを操作する時に利用する、正の数。
UNIXは全てのファイルとして扱うので、キーボードや端末画面すらファイルとして操作する。
その時に割り当てられているのがファイルディスクリプタ番号であり、0,1,2の三つは入出力に固定で割り当てられている

リダイレクション

リダイレクションは入出力先などを任意に変える仕組み。

lsコマンドの例

lsコマンドを普通に実行すると

1:シェルが標準入力(キーボード)からlsコマンドを実行しなさいという命令をうける
2:シェルがlsコマンドを実行し、その結果を受け取る
3:シェルは貰った結果を、標準出力(端末画面)に書きだす


ここで出力先をfileというファイルに変えてみましょう。

ls > file

このようにすると、標準出力先が端末画面からfileというファイルに変更され、lsコマンドの実行結果が、fileに書きこまれます。

標準入出力のリダイレクション一覧

  • > file
    • 標準出力の結果をファイルfileに書きこむ
  • >> file
    • 標準出力の結果をファイルfileに追加書き込みする
  • >m
    • ファイルディスクプタ番号m番の内容を標準出力にコピーする
    • 標準出力の内容をファイルディスクリプタm番に向ける
  • >&-
  • <&-

ファイルディスクリプタ番号を指定したリダイレクション

ファイルディスクリプタ番号の省略

今まで「ls > ls_list」や「cat < file」などと、「>」「<」を何気なく使ってましたが、これらはファイルディスクリプタ番号を省略した形であり、

command > file

command 1> file

と同義です。


また

command < file

command 0< file

と同義です。


0と1は良く使う&固定値なので、省略可能になっているわけです。

練習問題

今「file1」「file2」というファイルが存在するとします。
「file1」は読み込み可ですが、「file2」は読み込み不可です。

下記にコマンド例を書くので、それぞれ標準出力と標準エラー出力がどこに出力されるのか答えてみましょう

第一問
cat file* > result
第二問
cat file* 2> result
第三問
cat file* > result 2> result2
第四問
cat file* > result 2> /dev/null
第五問
cat file* > result 2> result
第六問
cat file* > result 2>> result
第七問
cat file* > result 2>&1
第八問
cat file* 2>&1 > result

練習問題の答え

第一問
cat file* > result
  • 標準出力
    • resultに出力
      • fileの内容がでる
  • 標準エラー出力
    • 端末画面
      • file2はpermission errで開けない旨
第二問
cat file* 2> result
  • 標準出力
    • 端末画面
      • file1の内容がでる
  • 標準エラー出力
    • resultに出力
      • file2はpermission errで開けない旨
第三問
cat file* > result 2> result2
  • 標準出力
    • resultに出力
      • file1の内容がでる
  • 標準エラー出力
    • result2に出力
      • file2はpermission errで開けない旨
第四問
cat file* > result 2> /dev/null
  • 標準出力
    • resultに出力
      • file1の内容がでる
  • 標準エラー出力
    • どこにもでない
      • /dev/nullにリダイレクションすると結果を捨てる
第五問
cat file* > result 2> result
  • 標準出力
    • resultに出力
      • file1の内容がでるが、すぐに消されるため確認できない
  • 標準エラー出力
    • resultに出力
      • file2を開けないエラーメッセージで、resultファイルを上書きする
第六問
cat file* > result 2>> result
  • 標準出力
    • resultに出力
      • file1の内容
  • 標準エラー出力
    • resultに追加出力
      • fiile1の内容に続いて、file2を開けないエラーメッセージを、resultファイルを追加書き込みする
第七問
cat file* > result 2>&1
  • 標準出力
    • resultに出力
      • file1の内容
  • 標準エラー出力
    • resultに出力
      • fiile1の内容に続いて、file2を開けないエラーメッセージを、resultファイルを書き込みする
第八問
cat file* 2>&1 > result
  • 標準出力
    • resultに出力
      • file1の内容
  • 標準エラー出力
    • 端末画面に出力
      • 端末画面に、file2を開けないエラーメッセージを出力

練習問題の補足

第六問と第七問の違い

第六問と第七問は結果が同じようにみえて、実は違います。
第六問は標準出力が終わってから、標準エラーを出力して追加書き込みしています。


たとえば、file1〜file100まであり、奇数の数字のファイルは読み込み不可だとしましょう。


すると、第六問のやり方では、file2〜〜file100までの偶数ファイルの内容がresultに書かれたのち、その次の行からfile1〜〜99の奇数ファイルが読み込めないエラーが書き込まれます。
一方、第七問のやり方では、標準出力の結果と標準エラー出力の結果が時系列できちんと一行ずつ書き込まれます。


多くの場合、第七問のやり方を利用する場合が多いでしょう。

第八問の解説

第八問の内部挙動の説明

  • まず、「2>&1」により、標準出力(stdout)の内容が標準エラー出力(stderr)にコピーされます
  • この段階では、標準出力も標準エラー出力も画面端末へ出力されます
  • その後、「> result」により、標準出力がリダイレクションにより result ファイルに出力されます
  • よって、標準出力はresultへ。標準エラー出力は端末画面へ。となります

OSの文字コードとlogの文字コードが異なり、tailなどで文字化ける場合の対処

あまりないかもしれないけど、実際に出くわしちゃったので、対処法をメモ

文字コード指定

オプションがいろいろある

-u	出力時にバッファリングを行わない
-j	JISコードに変換する
-e	EUCコードに変換する
-s	シフトJISコードに変換する
-w	UTF8コードに変換する

まぁ自分の環境に合わせてください


自分の.bashrcではaliasを

alias log='sudo tail -f /var/log/apache/error.log | nkf -u -w'

こんな感じにしてます。

debian lenny の screen にはutf8周りにバグがあるらしいので、入れ直したログ

具体的には、日本語を含む文字をコピペやら改行やらすると、表示がくずれる。
えらい盛大に。。。


UTF-8環境で GNU Screen の日本語表示が崩れる件 - OSのようなもの

Debian lenny導入メモ(on VMware Player), lenny上のruby 1.9.1のビルドでparse.cのコンパイルが固まる, screenとUTF-8の相性をなんとかする - ただのにっき(2009-02-07)

修正パッチがあるようなので、apt-getやめて入れ直す事に

sudo apt-get remove screen
※設定ファイルも残したくなければ、
sudo apt-get --purge remove screen

本体落としてきて

mkdir ~/tmp
cd tmp
wget ftp://ftp.uni-erlangen.de/pub/utilities/screen/screen-4.0.3.tar.gz
tar zxf screen-4.0.3.tar.gz
cd screen-4.0.3

パッチを持ってくる
screen install memo (UNIX)
たぶん、一番下のcjkwidth(chinese japanese korean width)ってのだけで大丈夫だと思うけど、とりあえず全部当ててみる

wget ftp://www.dekaino.net/pub/screen/screen-4.0.2-deadlock-patch
wget ftp://www.dekaino.net/pub/screen/screen-4.0.2-hankanacopy-patch
wget ftp://www.dekaino.net/pub/screen/screen-4.0.2-patch-cjkwidth-cvs-2006052001

パッチあてて

patch < screen-4.0.2-deadlock-patch 
patch < screen-4.0.2-hankanacopy-patch 
patch < screen-4.0.2-patch-cjkwidth-cvs-2006052001 

コンパイルに必要なもんを、apt-get で入れておきましょう

sudo apt-get build-dep screen

このまま configure すると、エラーになる

configure: error: !!! no tgetent - no screen

これは

sudo apt-get install libncurses5-dev

で解決

./configure  --enable-colors256 --prefix=/usr/local/ 
make
su
make install
とりあえずinstall完了

path通して
vi .bash_profile

 # add screen path
 PATH=$PATH:/usr/local/bin:

utf-8モードで起動する
vi .bashrc

alias s='screen -U' 

解決

よかったよかった

オブジェクトのメソッド一覧(メモ

クラスメンバはインスタンス生成して dump すれば見れるけど、クラスメソッド見る方法ぐぐったので、メモ。

$methods = get_class_methods("ClassName");

でいけるらしい。

$obj = new OBJ;
var_dump(get_class_methods(get_class($obj)));

メモ。

is_dirを含むスクリプトを他階層から実行する場合の注意 (自分用メモ

あるディレクトリのファイル一覧取得して、File か Directory かを調べる簡単なscript


check_dir.php は "/home/ryoff/prac_php/" にあるとする。

-- prac_php
   |-- check_dir.php
   |-- fuga.php
   `-- hoge
       `-- foo.php

vi check_dir.php

<?php

$dir = "/home/ryoff/prac_php/";
if (is_dir($dir)) {
  if ($dh = opendir($dir)) {
    while (($file = readdir($dh)) !== false) {
      if (is_dir($file)) {
        echo "DIR : " . $file . "\n";
      }
      else {
        echo "FILE: " . $file . "\n";
      }
    }
    closedir($dh);
  }
}

?>


これを、check_dir.php が置いてある階層以外からたたくと、

FILE: fuga.php
FILE: check_dir.php
DIR : ..
FILE: hoge
DIR : .

こんな感じで、hoge は Directory のはずなのに、File と認識されてしまった。

Perlでもこんなことあったな。

と思った。
自作モジュールをuseするときの注意点 - 雑想空間


Perlだと[FindBin]があるからいいんだけど、phpだとどうすればいいんだろう?

if (is_dir($dir.$file)) {

とりあえず、絶対パスにして応急処置ww
あとで調べる

php splitは非推奨になったようですね。(自分用メモ

$str = "a-b-c-d";


こんなのを/-/でsplitしたくなって、split関数つかったらwarningsでた。

PHP Deprecated: Function split() is deprecated in

「分かったよ。何かエラーでたらphp pro行けばいいんだろ」
ってことで、見てみると

警告
この関数は PHP 5.3.0 で 非推奨となりました。 この機能を使用しないことを強く推奨します。

と赤字で大きく書いてある。

注意: PHP 5.3.0 以降、 regex 拡張モジュールは非推奨となりました。かわりに PCRE 拡張モジュール を使うことが推奨されています。 この関数をコールすると E_DEPRECATED が発生します。 PCRE への変換についてのヘルプは 相違点の一覧 を参照ください。

なるほどね

splitの代わりに

ご丁寧にちゃんと代替方法が紹介されている。

Perl 互換の正規表現構文を使用する preg_split() は、往々にして split() よりも速い代替案となります。 正規表現の威力が必要ないのであれば、explode() を使用するほうがより高速です。これは正規表現エンジンの オーバーヘッドを受けません。

Perl の @chars = split('', $str) と同等の処理をする方法を知りたい場合は、 preg_split() あるいは str_split() の例を参照ください。

Perl互換の正規表現使いたいなら preg_split() で、特に必要なければ explode() ってことか。
str_split() は確かに文字を一文字ずつバラバラにするにはいいけど、それ以外には使いにくそう。

$st = "a-b-c-d";
print_r(preg_split('/-/', $st));
print_r(explode('-', $st));

何かしたいときに、その実現方法が組み込み関数にあるのかないのか調べるオーバーヘッドが高い・・。

Smartyクラスを継承して使いやすいように拡張。(php proの拡張セットアップがなぜかうまく動かなかったので・・。

拡張セットアップ - PHPプロ!マニュアル
こちらを参考に色々やってみた。
参考ソース

vi smarty_test/setup.php

<?php
require('Smarty.class.php');

class Smarty_Test extends Smarty {
  function Smrty_Test() {
    $this->Smarty();
    $this->template_dir = '(ファイルの絶対パス)/smarty_test/templates/';
    $this->compile_dir  = '(ファイルの絶対パス)/smarty_test/templates_c/';
    $this->config_dir   = '(ファイルの絶対パス)/smarty_test/configs/';
    $this->cache_dir    = '(ファイルの絶対パス)/smarty_test/cache/';
    $this->caching = true;
    $this->assign('app_name', 'Smarty Test');
  }
}
?>

呼び出し元は
vi smarty_test/htdocs/index.php

<?php
require_once('../setup.php');
$smarty = new Smarty_Test();
$smarty->assign('name','Ryoff');
$smarty->display('index.tpl');
?>

ほぼ(ってか全て)、php proからソース持ってきただけです


これ動かすと、

Warning: Smarty error: unable to read resource: "index.tpl" in /usr/local/lib/php/Smarty/Smarty.class.php on line 1093
Fatal error: Smarty error: the $compile_dir 'templates_c' does not exist, or is not a directory. in /usr/local/lib/php/Smarty/Smarty.class.php on line 1093

こんなエラーでてうまくいかねぇ。
var_dumpしてみると、コンストラクタうまく呼び出せてない感じ。


なんでだろう、って調べたら、どうやらこれは php4 の記述方法らしい。

php5では

Andante PHP、Smartyクラスを継承して拡張したい
↑のサイト様にお世話になりました。

parent::__construct(); //親クラス初期化

こんな感じで親のコンストラクタを明示的に呼び出せるらしい。


なので書き直してみた。
vi smarty_test/setup.php

<?php
require('Smarty.class.php');

class Smarty_Test extends Smarty {
  public function __construct() {
    parent::__construct(); //親クラス初期化
    $this->template_dir = '(ファイルの絶対パス)/smarty_test/templates/';
    $this->compile_dir  = '(ファイルの絶対パス)/smarty_test/templates_c/';
    $this->config_dir   = '(ファイルの絶対パス)/smarty_test/configs/';
    $this->cache_dir    = '(ファイルの絶対パス)/smarty_test/cache/';
    $this->caching = true;
    $this->assign('app_name', 'Smarty Test');
  }
}
?>

動いた。


非常に勉強になりました。