Windows Vista以降では、PowerShellが標準搭載されています。
慣れてくると、コマンドプロンプトよりもPowerShellの方がいろいろ便利です。とりあえず、うっかりlsと打っても大丈夫なのが一番重要。
ただし、起動手順は
・コマンドプロンプト: Windows + R → cmd
・PowerShell: Windows + R → powershell
このように、powershellと打ち込むのがめんどくさいですね。
そこで、ファイル名を指定して実行のショートカットに追加してしまいましょう。
C:/Users/baba/shortcuts などのフォルダを作り、その中にPowerShellへのショートカット「ps」という名前で作ります。
ps → C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe
後は、環境変数のPATHにC:/Users/baba/shortcuts を追加すればOKです。

環境変数のPATHを設定
これで、Windows + R → ps でPowerShellが開くため、コマンドプロンプトを使う機会が減りそうです。
ショートカットを作成する際、作業フォルダを指定できるのもポイントですね。
もちろん、PowerShell以外にもショートカットおき放題なので、ランチャーいらずです。
早速プライバシーマークが届きました!

Pマーク付与認定証
3日にBPSから速達で契約書を発送 → 6日にPマーク到着(ただし不在)なので、かなり迅速な手続きですね。
#今回も宅急便で届きましたが・・・ 大丈夫かな??
11日から正式に使えるので、名刺に組み込む準備中です。
バージョン情報と一口にいっても、実行ファイルのバージョン、DLLのバージョン、ClickOnceやインストーラのバージョンなど様々です。
バージョン情報ダイアログを作るために、これらのバージョンの取得方法を書いてみます。といってもコードで。
/// <summary>
/// ClickOnceで設定されたバージョンを取得
/// </summary>
public static Version ClickOnceVersion
{
if (!System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed)
{
return null;
}
return System.Deployment.Application.ApplicationDeployment
.CurrentDeployment.CurrentVersion;
}
/// <summary>
/// 実行中のexeファイルのバージョンを取得
/// </summary>
public static string AssemblyVersion
{
return System.Windows.Forms.Application.ProductVersion;
}
/// <summary>
/// 実行中のexeファイルと同一ディレクトリにあるDLLの情報を取得
/// </summary>
public static List<System.Diagnostics.FileVersionInfo> DllVersion
{
var versions = new List<System.Diagnostics.FileVersionInfo>();
//自身のファイルパスを取得し、同一ディレクトリのdllファイルを一覧する
string path = System.IO.Path.GetDirectoryName(
System.Windows.Forms.Application.ExecutablePath);
foreach (string name in System.IO.Directory.GetFiles(path, "*.dll"))
{
System.Diagnostics.FileVersionInfo info =
System.Diagnostics.FileVersionInfo.GetVersionInfo(name);
versions.Add(info);
}
return versions;
}
このような感じで、バージョン情報ダイアログが作れそうです。
※DLLのバージョン取得はもう少し工夫しないと問題がありそうですが・・・
C#等のアプリで、自身がClickOnceでインストール・実行されているかをチェックするには、
System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed
を調べます。
しかしこのプロパティ、普通に起動するとfalseが取得できますが、Visual Studioから起動するとなぜかフリーズすることがあります。ありました。
この場合は、ClickOnceアプリの実体が保存される C:/Users/ユーザ名/AppData/Local/Apps/2.0 を削除して、コンピュータを再起動すると直るようです。直りました。
起動プロセスが複雑になるとたまに面倒ですね。
SSHでパスワードなしログインするための環境構築で、基本的なところで躓いてしまいました。
■手順
- ssh-keygen -t rsa で、パスフレーズなしのSSH鍵を生成する
- ~/.ssh/id_rsa.pub を、リモートホストにコピーする
- リモートホストで、cat id_rsa >> ~/.ssh/authorized_keys する
- リモートホストで、authorized_keys のパーミッションを600にする
- ssh -i (IDファイル) (リモートホスト)
だけなのですが、authorized_keys の所有者がrootになってしまっていました。
パーミッションを600にしても、ownerが間違っていたらだめですよね・・・
ずいぶん長いことかかってしまいましたが、ようやくプライバシーマーク付与認定通知が届きました。
来週からBPSはプライバシーマーク付与事業者になる予定です。
この区切りに、審査からの数ヶ月で変化した点を踏まえ、再度個人情報取り扱いのレビューを行っていこうと思います。
引き続きよろしくお願いします。
#ところで、Pマーク事務局からの請求書・契約書がクロネコヤマトで届いたのですが、これって信書ですよね・・・?
PHPでmysql_queryの結果オブジェクトは、mysql_fetch_row()しても良いですが、mysql_fetch_array()した方が便利ですよね。
※mysql_fetch_row()では、結果を通常の配列で取得しますが、mysql_fetch_array()では、カラム名をキーにした連想配列で取得できます
EthnaでAppObjectを使わずにEthna_DBを直接たたく場合、結果オブジェクトはPEAR::DB_Resultオブジェクトです。
これはfetchRow()のメソッドを持ちますが、fetchArray()は存在しません。
http://pear.plus-server.net/package.database.db.db-result.fetchrow.html
カラム名をキーにした連想配列が欲しい場合、queryを投げる前にsetFetchMode(DB_FETCHMODE_ASSOC)を実行しておく必要があります。
http://pear.plus-server.net/package.database.db.db-common.setfetchmode.html
EthnaのデフォルトDBアダプタ、DB_PEARは、内部にPEAR::DBを持っているので、以下のようなコードを書けばOKです。
$db = $this->backend->getDB();
$db->db->setFetchMode(DB_FETCHMODE_ASSOC);
$res = $db->query($sql);
print_r($res->fetchRow());
※http://ethna.jp/ethna-document-dev_guide-db.htmlに
Ethnaの デフォルトの DB接続クラス(Ethna_DB_PEAR) はPEAR::DBを継承しているので
と書いてありますが、継承ではなくて委譲なので、$db->db->setFetchMode()となります。
社内のサーバを仮想化したのに伴い、社内wikiとして使っているdekiwikiも環境移行しました。
dekiwikiは優れたツールですが、移行やバージョンアップはいまいち考えられていない感もありますね。
次のような手順で移行しました。
・新サーバで、aptitudeから普通にdekiwikiをインストール(この際、DBパスワードなどを同じにしておく)
・旧サーバのmysqlデータを、新サーバにコピー
・/var/www/dekiwiki/attachments 以下を、新サーバにコピー
これで一通りは使えるようになります。
インストール後はコントロールパネルからキャッシュの再構築が必要です。
ついでにmonoをバージョンアップしたせいか、動作が劇的に速くなりました。古いmonoはCPUをずっと食い散らかしていたので、何かバグがあったのでしょう。
しかし、新wikiでは以下の問題が発生しました。
・検索すると画面が真っ白になる
・コントロールパネル「削除済みファイル」を開くと画面が真っ白になる
これは2つの問題が複合していました。
まず削除済みファイルの件ですが、attachmentsの中身のコピーが不完全で、エラーになっていたようです。
ファイルのインデックスはmysqlに保存されるため、インデックスが存在してファイル実体が無い場合、エラーになってしまいます。
/var/log/dekiwiki/deki-api.log に表示されるエラーを参考にしながら、完全なコピーを実施します。万が一ファイルを無くしてしまっていたら、とりあえず同名ダミーファイルでもおいておけば良いかもしれません。
次に検索が出来ない件ですが、PHPのメモリ不足でした。
php.iniでmemory_limit を16MBから64MBにしたら、無事検索できるようになりました。ページやファイル数が多くなっているため、結構メモリを食うのですね。
PHPエラーを出力する設定にしないと気づかないので注意が必要です。
MySQLのサブクエリは、一見問題なさそうなものも相関サブクエリに置換されて、極端に遅くなることがありますね。
INを適切に処理できないのが原因らしいです。
なぜMySQLのサブクエリは遅いのか。
今回は、以下のような「ユーザごとにスコアの履歴を保持していて、最新のスコアが有効」というテーブル設計で、「全ユーザの最新スコア合計を取得」しようとして問題が起きました。
score_log
| id |
int |
primary key auto_increment |
| user_id |
int |
※外部キー
|
| score |
int |
|
最初考えたSQLは以下のようなものです。
ユーザごとの最新スコアIDを一括取得し、それに該当するスコアの合計を計算しました。
SELECT SUM(score_log.score) FROM score_log WHERE score_log.id IN (SELECT MAX(log2.id) FROM score_log AS log2 GROUP BY log2.user_id)
しかしこれだと、EXPLAINをつけると分かるように、DEPENDENT SUBQUERYになってしまいます。
参考サイトによると、MySQLがINをEXISTSに変換する際に、相関サブクエリにしてしまっているようです。
実行速度も、データ件数の二乗に比例し、極端に遅くなってしまいました。
結果的に、以下のクエリに変更して解決しました。
SELECT SUM(score_log.score) FROM score_log, (SELECT MAX(log2.id) id FROM score_log AS log2 GROUP BY log2.user_id) each_cur WHERE score_log.id = each_cur.id
ユーザごとの最新スコアIDの一覧をテーブルとして、それをFROMに使ってしまうというわけですね。
PHPのfputcsv関数は便利です。
しかし、以下のような点が不便です。
エンコーディングを変換できない
主にCSV出力の目的になるExcelでは、Shift-JIS以外のCSVを開けません。
Excel 2007ならBOMを付けるとか、データインポートで実行するといった回避策はありますが、日本語のみならShift-JISで出力したいことが多いと思います。
このためにはあらかじめShift-JISに変換しておく必要があり、
$sjis = array_map(create_function(’$str’, ‘return mb_convert_encoding($str, “Shift-JIS”, “UTF-8″);’), $line);
といった処理をやっていましたが、正直めんどうです。
ダブルコーテーションのエスケープにバグがある
カンマが含まれる場合、ダブルコーテーションで囲ってくれます。
ダブルコーテーションが含まれる場合、ダブルコーテーションを二重にしてエスケープしてくれます。
しかし、\” (バックスラッシュ+ダブルコーテーション)があった場合、なぜかこの部分のダブルコーテーションは二重にしてくれません。
ということで、結局自前でやった方が便利です。
mb_str_replaceは、http://fetus.k-hsu.net/document/programming/php/mb_str_replace.htmlを使わせて頂きました。
function _fputcsv($fp, $data, $toEncoding='Shift-JIS', $srcEncoding='UTF-8') {
require_once 'mb_str_replace.php';
$csv = '';
foreach ($data as $col) {
if (is_numeric($col)) {
$csv .= $col;
} else {
$col = mb_convert_encoding($col, $toEncoding, $srcEncoding);
$col = mb_str_replace('"', '""', $col, $toEncoding);
$csv .= '"' . $col . '"';
}
$csv .= ',';
}
fwrite($fp, $csv);
fwrite($fp, "\r\n");
}
ちゃんとテストしていないのでミスがあるかもしれませんが、このくらいシンプルで十分そうですね。