はてなブックマーク - ディレクトリ内のファイル容量を調べる方法(CUI)
    このエントリーをはてなブックマークに追加

    Fusic 北岸です。

    もう今年も終わりますね。
    休みの日に近所でバドミントンをしているのですが、
    昨日で今年最後の分が終わってしまいました。
    打ち損じでカツーンなんて音をさせてばかりではありますが、来年も楽しく頑張ります!

    さてさて、今回のテーマは 「CUIでファイルサイズの合計を調べる」 です。
    すごく基本的な内容ではありますが、以前ちょっと気になって調べたのでまとめておきます。

    ファイルサイズを調べるコマンド

    皆さんは、ファイルサイズを調べる時に、どのようなコマンドを使用されていますか?
    私がすぐに思いつくのは、次のコマンドです。

     $ ls -l

    そして、ディレクトリの容量を調べるなら、このコマンドですね。

     $ du

    しかし、この du というコマンド、きちんとファイルサイズを測れません。

    この du は、ブロックサイズを単位としてサイズを計測しています。

    今回用意したシステムは ブロックサイズが4096バイト(=4.0KB) なので、
    例えば次のような結果が得られます。

    sky@ub1110x64:~/contrail$ ls -l ./substance/
    合計 12
    -rw-rw-r-- 1 sky sky 4097 2011-12-26 07:22 ice_grain
    -rw-rw-r-- 1 sky sky 4096 2011-12-26 07:22 moisture
    sky@ub1110x64:~/contrail$ du -ah ./substance/
    4.0K	./substance/moisture
    8.0K	./substance/ice_grain
    16K	./substance/

    たった1バイトしか違わないのに、du コマンドだと 4.0KB も違う事になっていますね。

    どうやってファイルサイズを測るか

    では、どうすればブロックサイズに左右されずに、
    フォルダ内の容量を測れるのでしょうか。

    調べて分かったのは、 find コマンドです。
    一例を示します。

     $ find ./ -printf "%s\n"

    階層的にディレクトリ内ファイルを検索してくれるコマンド find には、
    そのファイルに関する情報を出力してくれるアクションがあります。

    それが 「-printf」 です。

    「 find ./ 」のみだと、カレントディレクトリ配下のディレクトリの内容を出力するだけですが、
    「-printf」を利用する事により、出力内容を変える事が出来ます。

    ここで使用しているフォーマットは次の通りです。
    %s : ファイルサイズのバイト表示
    \n : 改行

    実行結果の一例を示します。

    sky@ub1110x64:~/contrail$ ls -l *
    sky:
    合計 24
    -rw-rw-r-- 1 sky sky 6789 2011-12-26 07:22 high
    -rw-rw-r-- 1 sky sky 1234 2011-12-26 07:22 light
    -rw-rw-r-- 1 sky sky 9999 2011-12-26 07:22 scraper
     
    substance:
    合計 12
    -rw-rw-r-- 1 sky sky 4097 2011-12-26 07:22 ice_grain
    -rw-rw-r-- 1 sky sky 4096 2011-12-26 07:22 moisture
    sky@ub1110x64:~/contrail$ find ./ -printf "%s : %f\n"
    4096 : ./
    4096 : sky
    1234 : light
    6789 : high
    9999 : scraper
    4096 : substance
    4096 : moisture
    4097 : ice_grain

    ※見やすいようにファイル名(%f)も表示しています。

    sky@ub1110x64:~/contrail$ du -ah
    4.0K	./sky/light
    8.0K	./sky/high
    12K	./sky/scraper
    28K	./sky
    4.0K	./substance/moisture
    8.0K	./substance/ice_grain
    16K	./substance
    48K	.

    ちなみに、次のようにディレクトリ指定を省略すれば、
    カレントディレクトリ配下を検索するため、同じ出力が得られます。

     $ find -printf "%s : %f\n"

    現状のままでは、各ファイルやディレクトリのサイズが列挙されただけで、合計の値が得られていません。
    もう一手間加えます。

    というわけで、 awk コマンドを組み合わせたものがこちらです。

    sky@ub1110x64:~/contrail$ find ./ -printf "%s\n" | awk '{ sum += $1; }; END { print sum }'
    38503

    各ファイルサイズについて、{ sum += $1; }; の部分で次々と足し合わされ、
    END { print sum } の部分によって、最後に1回だけ、変数 sum が出力されます。

    いかがでしょうか。
    私にとって、awk コマンドはまだまだ慣れないものではありますが、
    とても応用が効きそうなコマンドだなと思いました。

    物理をやってたあの頃

    研究室を思い出すと、、、やっぱりまだありました、変なもの、不思議なもの。
    前回は片栗粉でしたが、今回は ペットボトル です。

    それは エッキー って呼ばれてました。
    中身は、500ml いっぱいの水と、砂が4分の1程度、ピンが3、4個ほどだったと思います。

    では、何のためのものなのかと言うと 「液状化現象」 を見るためです。
    液状化っていうと、地震の時に水がしみ出してくるアノ現象ですね。

    使い方としては、
    このエッキーをよく振って、静かに置きます。
    この時、ピンは砂に埋もれて見えない状態になっているはずです。

    この状態で、デコピンなどしてエッキーに刺激を与えると、ピンが飛び出してきます。
    これが、地震後の液状化に対応しています。

    ちなみに最近知ったのですが、
    エッキーって、商品名でした。
    どうも、キットとして売られているようです。

    おしまいです。
    ではでは。

      はてなブックマーク - CakePHP2.0で現状の問題点などまとめました。
      このエントリーをはてなブックマークに追加

      そろそろ誕生日の萩原です。誕生日プレゼントください。去年も似たようなことを言ったような気がするのは気のせいです。

      最近、CakePHP2を色々触ってまして、色々困ったことがあったので、まとめておこうかなと思います。(CakePHP2.0.4現在)
      (下でCakePHP2.xなんか知りませんと言われてますが・・・)

      AppControllerをapp/Controller以外に置く場合の注意

      CakePHP2からは皆さんご存知の通り、AppController及び、AppModelが、app直下に設置が出来なくなりました。

      CakePHP2ではAppControllerはapp/Controller以下に設置をすること、と言うことになっています。

      しかし、個人的には出来れば、直接アクセスするコントローラとはAppControllerは分けたいところです。

      そこで、AppControllerをapp/Libに設置をしようと考えまして設置を行っていました。

      app/Lib以下にAppControllerを設置後は、AppControllerを継承するコントローラにはApp::usesの記述が必要になります。

      <?php
       
      App::uses(&#039;AppController&#039;, &#039;Lib&#039;);
      
      class PostsController extends AppController {
       
      }

      しかし、作業をしていく内に、たまにAppController内のアクションが読めないというエラーが出ることがありました。(debug2でも)

      元凶はapp/tmp/cache/persistent/cake_core_file_mapのキャッシュ(こいつがなかなかの曲者です・・・)だと言うことはすぐにわかったのですが、どうにも再現性も取れず、対策がなかなかつかめない状態でした。(1ヶ月くらい悩んでいました・・・)

      結局のところ現象としては

      (1)404ページ(missing controllerなど)にアクセス
      (2)CakeErrorControllerを見に行く
      (3)CakeErrorControllerがapp/Controller以下にセットしていない場合は、CakeコアのCakeErrorControllerを見に行く
      (4)当然、コアのコントローラにApp::usesの記述があるはずもなく、継承するAppControllerの場所としてApp/Controller以下を見に行く
      (5)ないので、CakeコアのAppControllerにアクセスしに行く
      (6)さらに、キャッシュにAppControllerの位置はCakeコアにある、と記述
      (7)その後、通常のシステムのページにアクセスしても、AppControllerとして、CakeコアのAppControllerを見に行く
      (8)App/Lib以下に設置したAppConttroller内のアクションが呼ばれない

      ということが起こっていました。

      解決策としては

      ①AppControllerを素直にApp/Controller内に設置する
      ②CakeErrorControllerをApp/Controller内にコピーしてApp::usesをセットする

      ①はまずまず問題ないく動作をするはずです。

      ②についてはCakeコアでAppControllerを継承しているものが、他には通常あまり使わないであろう、PagesControllerくらいだったので、こちらでもおそらく問題なく動作すると思います。

      (2011/12/15追記)

      LibにAppControllerを置くと言う件について、Libの中にControllerのディレクトリを作ってその中に置けばいいという指摘を頂きました。
      試してみると、app/Lib/Controller/AppController.phpという形で設置をしたら、確かに問題なくusesなしでもapp/Lib/Controller/AppController.phpを見に行ってくれました。
      これならCakeErrorを書き換える必要もなくなるので、これが一番良い解決法かもしれません。

      app以下のクラス名とPlugin以下のクラス名が同名だと誤動作する

      これは具体例を挙げた方が早いと思います。

      app/Controller/PostsController.php(①)と、app/Plugin/Post/Controller/PostsController.php(②)の二つの同名のコントローラがあるとします。

      初めに①にアクセスした後、②にアクセスをしようとすると、①にアクセスをしてしまうというものです。(逆もしかり)

      これが起きてしまう原因はapp/tmp/cache/persistent/cake_core_file_mapのキャッシュがプラグインを考慮していないためです。(またお前か・・・)

      実のところ、AdminPluginを作成してそこに管理画面を実装しようとしていたところでしたので、これはかなり致命的でした。

      解消方法自体はコアを触れば出来なくもないのですが、コアのアップデートなど一切できなくなるのでお勧めはしません。

      どうしても動作させたいのであれば、lib/Cake/Core/App.phpをプラグインを考慮するように直接編集すると、動作はします。

      まぁ、ただ当分はapp以下とPlugin以下で同名のクラスが入るような実装は避けた方が良いでしょう。

      (2011/12/16追記)

      CakePHPチームにチケットを投げてみましたが、同名のクラスは現在使えない、Cake3で計画中と言われました。

      PaginatorのorderにSQLが記述できない

      CakePHP2からPaginatorがコンポーネントに分離されたことは皆さんご存知かとは思いますが、CakePHP2では現在のところPaginatorでorderにSQLがそのまま記述が出来ません。

      例えば

      $this->Paginator->settings = 
          array(&#039;order&#039; => array(&#039;`hoge` is null ASC&#039;)):

      のようなソートを行いたい時に上記のorderが無視されます。(実際に必要な場面がありました・・・)

      これは、コアのPaginatorComponentを見ればわかるのですが、352行目~の記述で

      if ($object->hasField($field)) {
      	$order[$alias . &#039;.&#039; . $field] = $value;
      } elseif ($object->hasField($key, true)) {
      	$order[$field] = $value;
      } elseif (isset($object->{$alias}) && $object->{$alias}->hasField($field, true)) {
      	$order[$alias . &#039;.&#039; . $field] = $value;
      }

      とありまして、例外が全く、無視される形になっています。

      この記述自体は1.3にもあるのですが、1.3では最後にarray_mergeをしているので、SQLも取り込んでくれていたのですが、2.0では無視された状態です。

      どうしても特殊なorderをかけたい場合は現状では素書きのSQLやDBのviewなどで対応しておいた方が良いかもしれません。

      (今回私の場合は、元々orderをかけたかったテーブルがviewだったのでviewにソート用のカラムを泣く泣く追加しました・・・)

      (2011/12/16追記)

      CakePHPチームにチケットを投げてみたところ、orderを文字列で指定したら動くじゃん!って言われたので現在配列で指定したんだけど・・・と返しているところです。

      SSLページでのCookieのセキュア属性を外す方法

      SSLページがあるページとないページが混同する場合、Cookieのセキュア属性が付いていると、セッションを共有できないため、セキュア属性を外してあげる必要があります。

      1.3の場合は以下の対応が必要になります。

      app/config/core.php

      //	Configure::write(&#039;Session.save&#039;, &#039;php&#039;);
      	Configure::write(&#039;Session.save&#039;, &#039;session_custom&#039;);

      app/config/session_custom.php

      <?php
       
      if (empty($_SESSION)) {
          ini_set(&#039;session.use_trans_sid&#039;, 0);
          ini_set(&#039;session.name&#039;, Configure::read(&#039;Session.cookie&#039;));
          ini_set(&#039;session.cookie_lifetime&#039;, $this->cookieLifeTime);
          ini_set(&#039;session.cookie_path&#039;, $this->path);
      }
      ini_set(&#039;session.cookie_secure&#039;, 0);

      これは、コアのcake_session.phpの設定を強引に上書きをしています。

      CakePHP2ではcore.phpに以下の記述をすれば、対応できます。

      Configure::write(&#039;Session&#039;, array(
          &#039;defaults&#039; => &#039;php&#039;,
          &#039;ini&#039; => array(
              &#039;session.cookie_secure&#039; => 0
          )
      ));

      特に新しいファイルを作成する必要はなく、手軽です。

      CakePHP2.0ではセッションを共有するしないにかかわらず、SSLがあるページとないページが混同するサイトでは、セキュア属性は外した方が良い。

      これは、まずSSLのあるページ(①)でログインをした後、SSLのないページ(②)にアクセスし、その後、①のページにアクセスをすると、ログアウトしてしまうと言う現象のためです。(これは1.3では発生しません)

      詳しい原因についてはまだ追い切れていないのですが、①にアクセスした後、②にアクセスすると①と異なるセッションキーが振られます。(これは当然の話)

      その後①のページにアクセスすると、②で発行されたセッションキーに上書きされてしまうため、ログアウトをしてしまいます。

      以上の理由から、今のところ、SSLがあるページとないページが混同するサイトでは、セキュア属性は外して置いた方が良いと思います。

      (2011/12/16追記)

      CakePHPチームにチケットを投げてみたところ、上記の書き方でセキュア外せばいいじゃん!って言われました。

      最後に

      まだCakePHP2は駆け出しで、問題が色々と出てますが、触っている感じとしては1.3とそう大差なく使えています。そして早いです。

      きっとこれからどんどん良くなっていくと思うので皆さんどんどんさわりましょう!

      後、本当にfile_mapのキャッシュには要注意です!
      ああ・・・app.phpの750行目を消してfile_mapのキャッシュをなかったことにしたい・・・w

        はてなブックマーク - CakePHPでのSQLのログ
        このエントリーをはてなブックマークに追加

        Fusicの中野です。

        表題のとおり、CakePHPのログに関する小ネタを紹介します。
        ちなみに、使っているCakePHPのバージョンは1.3です。(CakePHP2.xなんて知りません)

        CakePHPでConfigure::read(“debug”) > 1の場合は画面にSQLのログが表示されます。
        ですが、運用中ではPHPのエラー表示などがされないように、Configure::read(“debug”) == 0の状態ではないかと思います。
        そうなると、すこしだけSQLの確認をしたいときなどちょっと面倒ですよね。

        CakePHPはConfigure::read(“debug”) > 1の場合、SQLのログはDboSourceの_queriesLogというメンバの配列に格納されていて、どうやらその配列の中身を画面に出力しているようです。

        そこで、こいつの中身が取れれば解決できるので・・・

        $db =& ConnectionManager::getDataSource($Model->useDbConfig);
        $db->fullDebug = true; // ここがfalseだとログを保存しない。$db->fullDebug = Configure::read() > 1みたいになってます。
        $db->_queriesLogMax = 200; // SQLがこの件数こえると配列に保存しないよ的なもの。

        みたいなことをfindを実行する前に書いてあげれば、_queriesLogに登録されるようです。

        だけど、これだと少々めんどいのでBehaviorにまとめてみましょう。

         
        class QueryLogBehavior extends ModelBehavior {
          var $__settings = array();
          var $_queriesLogMax = 999;
         
          function setup( &$Model, $settings = array() ){
           $this->__settings[ $Model->alias ] = array();
         }
         
          function make_query_key( $Model, $query ){ return get_class( $Model ) . "__" .  md5( serialize( $query ) ); }
          function add_query( $key ){
            return "&#039;" . $key . "&#039; = &#039;" . $key . "&#039;";
          }
         
          function beforeFind( &$Model, $query ){
            if( array_key_exists( "log", $query ) and $query["log"] ){
              $db =& ConnectionManager::getDataSource($Model->useDbConfig);
              $db->fullDebug = true;
              $db->_queriesLogMax = $this->_queriesLogMax;
              $key = $this->make_query_key( $Model, $query ); // いらないかも。
              if( array_key_exists( "conditions", $query ) ){
                $query["conditions"][] = $this->add_query( $key );
              }
              else {
                $query["conditions"] = array( $this->add_query( $key ) ); 
              }
              $this->__settings[$Model->alias][] = array( $key, $query["log"] );
            }
            return $query;
          }
         
          function afterFind( &$Model, $results, $primary ){
            $db =& ConnectionManager::getDataSource($Model->useDbConfig);
            $db->fullDebug = Configure::read() > 1;
            $db->_queriesLogMax = 200;
         
            $hit_sql = null; $level = LOG_DEBUG;
            foreach( $this->__settings[$Model->alias] as $keypair ){
              list( $key, $level ) = $keypair;
              foreach( $db->_queriesLog as $index => $query_log ){
                if( preg_match( "/^(.*)" . $this->add_query( $key ) . "(.*)$/", $query_log["query"], $regexp )) {
                  $hit_sql = $regexp[1] . "" . $regexp[2];
                  break;
                }
              }
            }
            if( !is_null( $hit_sql ) ) $this->log( $hit_sql, $level );
            return true;
          }
        }

        なんかこんな風に適当にBehaviorを追加して

        $this->Hoge->find( "all", array( "contain" => array(), "conditions" => <なんかの条件>, "log" => LOG_INFO ) );

        みたいにしてあげたら、app/tmp/log/debug.logにSQLが記述されます。これで任意のfindの処理のSQLをログにだしたりできます。
        データベースの設定でSQLのログだしとけよとかいうツッコミはなしで。