ペチパーノート

WEB開発系Tipsブログです。

コマンドを定期的に繰り返す

watchコマンドで定期的にコマンドを実行することができます。

1秒間隔でls -lを実行する

$ watch --interval 1 ls -l
Every 1.0s: ls -l                                                      Thu Jun 12 23:36:24 2014

合計 0

別ターミナルからファイルを作成してみます。

$ touch test

作成されたファイルが見えました!

Every 1.0s: ls -l                                                      Thu Jun 12 23:40:09 2014

合計 0
-rw-rw-r-- 1 wwwadmin wwwadmin 0  6月 12 23:39 2014 test

ディスクの容量を一時的に監視したりすることもできますね。 応用次第ではいろいろできそうです。

tailでログを監視する際、特定行を除外する

tailでアクセスログの監視をする場合、特定文字列が含まれる行を排除したい場合があります。
そんな時は正規表現で対象パターンを含まない行を抜き出します。

例) jpg,png,cssが含まれる行を除外

$ tail -f access.log | egrep -v 'jpg|png|css'

これで関係ないログで迷子になるのを防げます(笑)

ジオメトリ型のカラムを用いて2点間の距離を求める

位置情報をMySQLに保存し、2点間の距離を調べたい場合、どうしたらよいのだろう?

f:id:butterbull:20140612183119j:plain

とにかくやってみる

テーブルを作成

CREATE TABLE places (
  id int(11) NOT NULL,
  name varchar(32) NOT NULL,
  latlon geometry NOT NULL,
  PRIMARY KEY (id)
);

POINT型(緯度経度)で場所を登録

INSERT INTO places VALUES (1, '岩手県庁', GeomFromText('POINT(39.703595 141.152526)'));
INSERT INTO places VALUES (2, '岩手県水産会館', GeomFromText('POINT(39.703736 141.153755)'));

素直にSELECTしてみると文字化けしました。 ジオメトリ型にはバイナリが入ってます。

mysql> select * from places;
+----+-----------------------+---------------------------+
| id | name                  | latlon                    |
+----+-----------------------+---------------------------+
|  1 | 岩手県庁              |        P・fレC@Eケ4~皃a@       |
|  2 | 岩手県水産会館        |        IpレC@・a@       |
+----+-----------------------+---------------------------+
2 rows in set (0.00 sec)

ASTEXTをかませばOK

mysql> SELECT id, name, ASTEXT(latlon) FROM places;
+----+-----------------------+-----------------------------+
| id | name                  | ASTEXT(latlon)              |
+----+-----------------------+-----------------------------+
|  1 | 岩手県庁              | POINT(39.703595 141.152526) |
|  2 | 岩手県水産会館        | POINT(39.703736 141.153755) |
+----+-----------------------+-----------------------------+
2 rows in set (0.00 sec)

経度だけ、緯度だけを取得するときはX、Yをかまします。

mysql> SELECT id, name, ASTEXT(latlon), X(latlon), Y(latlon) FROM places;
+----+-----------------------+-----------------------------+-----------+------------+
| id | name                  | ASTEXT(latlon)              | X(latlon) | Y(latlon)  |
+----+-----------------------+-----------------------------+-----------+------------+
|  1 | 岩手県庁              | POINT(39.703595 141.152526) | 39.703595 | 141.152526 |
|  2 | 岩手県水産会館        | POINT(39.703736 141.153755) | 39.703736 | 141.153755 |
+----+-----------------------+-----------------------------+-----------+------------+
2 rows in set (0.00 sec)

あとはプログラムでごにょごにょしてもらい
最終的に以下のようなSQLを発行すると2点間の距離が求められました。

mysql> SELECT Glength(GeomFromText('LineString(141.152526 39.703595, 141.153755 39.703736)')) ;
+---------------------------------------------------------------------------------+
| Glength(GeomFromText('LineString(141.152526 39.703595, 141.153755 39.703736)')) |
+---------------------------------------------------------------------------------+
|                                                            0.001237061841618116 |
+---------------------------------------------------------------------------------+
1 row in set (0.00 sec)

ただし、この値の単位は(1度 = 112.12km)
kmにするには112.12をかけて、さらにmにするには1000をかけます。

mysql> SELECT Glength(GeomFromText('LineString(141.152526 39.703595, 141.153755 39.703736)')) * 112.12 * 1000 AS meter;
+--------------------+
| meter              |
+--------------------+
| 138.69937368222315 |
+--------------------+

約138メートルでした。

MySQLのトランザクション処理中にALTERするとコミットされる

トランザクションが効かずハマったことが何度かあります(笑)
InnoDBのつもりがMyISAMで作ってたとか。。

あとはトランザクション開始後に特定のDDLを発行すると
暗黙的にコミットされます。
ってか知らなかったけどされちゃいました。

やってみる

ユーザーテーブルがあります。

mysql> select * from users;
+----+------+
| id | name |
+----+------+
|  1 | taro |
+----+------+
1 row in set (0.01 sec)

トランザクションを開始します。

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

名前を変更します。

mysql> update users set name='jiro';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

ロールバックします。

mysql> rollback;
Query OK, 0 rows affected (0.17 sec)

戻りました。

mysql> select * from users;
+----+------+
| id | name |
+----+------+
|  1 | taro |
+----+------+
1 row in set (0.00 sec)

今度は途中でALTER挟んでみます

ユーザーテーブルがあります

mysql> select * from users;
+----+------+
| id | name |
+----+------+
|  1 | taro |
+----+------+
1 row in set (0.00 sec)

トランザクション開始します

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

名前を変更します

mysql> update users set name='jiro';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

オートインクリメント値を変更します

mysql> alter table users auto_increment=1;
Query OK, 1 row affected (0.41 sec)
Records: 1  Duplicates: 0  Warnings: 0

ロールバックします

mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

戻りません

mysql> select * from users;
+----+------+
| id | name |
+----+------+
|  1 | jiro |
+----+------+
1 row in set (0.00 sec)

データ全削除後にIDを1からにしたい。
そんな健気な思いがこのバグを産んだのだと思います。合掌

ケータイで撮った写真をPHPで回転する

サーバに画像をアップロードさせるサービスを構築する場合
ケータイで撮った写真などが横になってる事があります。
そんな時はシステム側で調整してやると親切かと思います。

画像には様々な情報(EXIF)が保存されているので、
画像方向情報(Orientation)を見て回転させてやります。

※拡張モジュールのImageMagickExifを使用します。

test.php

<?php
$file = 'test.jpg';
fixOrientation($file);

function fixOrientation($file)
{
    $is_exists = file_exists($file);
    if ($is_exists === false) {
        return false;
    }

    $exif = exif_read_data($file);
    $orientation = isset($exif['Orientation']) ? $exif['Orientation'] : 0;

    $image = new Imagick($file);
    switch ($orientation) {
        // EXIF情報なし
        case 0:
            return;
        // そのままでOK
        case 1:
            return;
        // 上下反転
        case 2:
            $image->flopimage();
            break;
        // 180度反転
        case 3:
            $image->rotateimage(new ImagickPixel(), 180);
            break;
        // 左右反転
        case 4:
            $image->flipimage();
            break;
        // 270度回転&上下反転
        case 5:
            $image->rollimage(new ImagickPixel(), 270);
            $image->flipimage();
            break;
        // 90度回転
        case 6:
            $image->rotateimage(new ImagickPixel(), 90);
            break;
        // 90度回転&上下反転
        case 7:
            $image->rotateimage(new ImagickPixel(), 90);
            $image->flipimage();
            break;
        // -90度回転
        case 8:
            $image->rotateimage(new ImagickPixel(), -90);
            break;
    }

    $image->writeimages($file, true);
    $image->destroy();
}

処理のメモリ使用量を取得する

膨大なデータを扱うスクリプトで下記のようなメモリ確保エラーが出ることがあります。

Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 32 bytes) 

下記のようにPHPの設定でメモリ使用量を増やして回避させることもできますが、
根本的にプログラム設計をやり直した方がいいことがほとんどです。

ini_set('memory_limit', '512M');

これで一旦様子見るか!ってことにすると大抵後で泣きます^^;

そんな時はメモリ使用量を取得できるmemory_get_usage()の出番です。

test.php

<?php
$mem_usage_start = memory_get_usage();
$array = range(0, 999);
$mem_usage_end = memory_get_usage();

echo 'usage:'. ($mem_usage_end - $mem_usage_start);

ブラウザの出力

usage:144608

処理前と処理後でメモリ使用量を取得し、差分を表示しています。
これで$array = range(0, 999);がどのくらいメモリを使用しているかがわかります。

ちなみにスクリプトの最大メモリ使用量はmemory_get_peak_usage()で取得することができます。

呼び出し元の情報を取得する

関数の中で関数を呼んでさらに・・・
で結局どういう流れで呼ばれてるの?うぉー帰りたい!!
ってことあります。

そんなときはdebug_backtrace()です。

test.php

<?php
function speak($word) {
    tell($word);
}

function tell($word) {
    propagate($word);
}

function propagate($word) {
    echo $word;
    print_r(debug_backtrace());
}

speak('これはテストです');

ブラウザの出力

これはテストです
Array
(
    [0] => Array
        (
            [file] => /var/www/html/test.php
            [line] => 7
            [function] => propagate
            [args] => Array
                (
                    [0] => これはテストです
                )

        )

    [1] => Array
        (
            [file] => /var/www/html/test.php
            [line] => 3
            [function] => tell
            [args] => Array
                (
                    [0] => これはテストです
                )

        )

    [2] => Array
        (
            [file] => /var/www/html/test.php
            [line] => 15
            [function] => speak
            [args] => Array
                (
                    [0] => これはテストです
                )

        )

)

今回は1ファイルに複数の関数を記述して実行していますが
共通関数ファイル等を読み込み、その中の関数を実行する場合でも有効です。