vmstat/mpstatのようなものを作ってみた
PowerShellの勉強として、vmstat/mpstatのようなものを作ってみました。例えば、pstat.ps1という名前で保存すれば、./pstatで1秒毎のCPU使用率が表示されます。
param($delay = 1) $allPc = new-object System.Diagnostics.PerformanceCounter( "Processor", "% Processor Time", "_Total") $usrPc = new-object System.Diagnostics.PerformanceCounter( "Processor", "% User Time", "_Total") $sysPc = new-object System.Diagnostics.PerformanceCounter( "Processor", "% Privileged Time", "_Total") $idlPc = new-object System.Diagnostics.PerformanceCounter( "Processor", "% Idle Time", "_Total") Write-Output "all usr sys idl" # 初回は意味をなさないので値を取得するだけで捨てる $all = $allPc.NextValue() $usr = $usrPc.NextValue() $sys = $sysPc.NextValue() $idl = $idlPc.NextValue() while($true){ Start-Sleep -s $delay $all = $allPc.NextValue() $usr = $usrPc.NextValue() $sys = $sysPc.NextValue() $idl = $idlPc.NextValue() Write-Output ([string]::Format("{0,3:f0} {1,3:f0} {2,3:f0} {3,3:f0}", $all, $usr, $sys, $idl)) }
初心者がはまるPowerShellの罠
PowerShellを使い始めたのですが、Unixのシェルや一般的なスクリプト言語とはいくつか異なる点があってはまりましたので、メモに残しておきます。
- エスケープシーケンスはバックスラッシュではなく、バッククォート。改行は\nではなく、`n。
- `やその他明らかに継続するだろうとわかる場合(例:行末が|)だと行継続になる。ただし、行継続モードになるので解除には空行が必要。
- Set-ExecutionPolicy RemoteSigned を実行しないと、スクリプトは許可されない。
Groovyではstatic importがload / requireの代わりになる
groovyshで作業していると、ちょっとした作業はメソッドを作っておいて後から呼び出したいと言うことがあります。ところが、Groovyにはloadとかrequireが無いので、groovyshを起動するたびにコピー&ペーストしていました。
これじゃあ使いにくいからloadやrequireが欲しいと言ったら、id:nowokayさんからstatic importを教えてもらいました。
さっそく試してみたのでメモに残しておきます。
以下の内容で Hello.groovy というファイルを作ります。ポイントはsayHelloをstaticとして定義しているところ。
def static sayHello(name) { println "Hello, ${name}!" }
これをgroovycでコンパイルします。
groovyc Hello.groovy
groovyshで実行するとこの通り。
groovy:000> import static Hello.* groovy:000> sayHello("World") Hello, World! ===> null
Sequelはいつの間にか良くなっていた
以前、SequelからJDBC経由でデータベースに接続する場合は注意すべきだと書きました。
- SequelはJDBC以外ならいい (2008-07-10)
Sequelはまだまだ使い物にならないと思っていましたが、いつの間にか良くなっていました。
かつては、sequel_core/sql.rbに以下のような記述がありました。
# This method quotes the given name with the SQL standard double quote. # It uppercases the name given to conform with the SQL standard. This # should be overridden by subclasses to provide quoting not matching the # SQL standard, such as backtick (used by MySQL and SQLite), or where # lowercase is the default for unquoted identifiers (PostgreSQL). # # If you are using a database such as Oracle that defaults to uppercase # but you are using lower case identifiers, you should override this # method to not upcase the name for those identifiers. def quoted_identifier(name) "\"#{name.to_s.upcase}\"" end
おせっかいにも、quoted_identifierメソッドは名前を大文字に変換していたのです。
このため、PostgreSQLのように大文字と小文字を区別し、デフォルトでは小文字を使うデータベースの場合、DB[:user_data].allといったシンボルによる表指定ができませんでした。
この問題の回避策として、JDBC経由でPostgreSQLに接続するときは、以下のような小細工をしていました。
module Sequel module JDBC class Dataset < Sequel::Dataset def quoted_identifier(name) "\"#{name.to_s}\"" end end end end
Sequel 2.8.0では、sequel_core/sql.rbは以下のようになっています。
# This method quotes the given name with the SQL standard double quote. # should be overridden by subclasses to provide quoting not matching the # SQL standard, such as backtick (used by MySQL and SQLite). def quoted_identifier(name) "\"#{name}\"" end
quoted_identifierは余計なことをしなくなりました。
というわけで、改めてSequelからJDBC経由でPostgreSQLに接続するサンプルを書くと、以下の通りになります。
require 'rubygems' require 'sequel' DB = Sequel.connect('jdbc:postgresql://localhost/postgres?' + 'user=postgres&password=postgres') DB["SELECT * FROM user_data"].all # 成功 DB[:user_data].all # 成功(従来は失敗) DB[:user_data].all.each do |user_data| puts "#{user_data[:user_id]} #{user_data[:user_account]}" end; nil
JDBCドライバの登録も自動化され、すっきりしました。
jirbで日本語を扱うには、jirb_swing -Kuとする
jirbに直接日本語を入力しようとすると、文字化けしてしまいます。
C:\>jirb irb(main):001:0> '???{??' => "\302\223\303\272\302\226{\302\214\303\252" irb(main):002:0> java.lang.String.new('???{??').to_s => "\302\223\303\272\302\226{\302\214\303\252"
--noreadline オプションを付けて行編集機能を無効にすると、日本語が入力できるようになりますが、評価結果が文字化けしてしまいます。
C:\>jirb --noreadline irb(main):001:0> '日本語' => "\223\372\226{\214\352" irb(main):002:0> java.lang.String.new('日本語').to_s => "\357\277\275\357\277\275{\357\277\275\357\277\275"
-K オプションでSJISを宣言すると、文字列の評価結果は文字化けしませんが、JavaのStringクラスに渡した結果は文字化けしてしまいます。Javaに値を渡す際に、SJISからUnicodeへのコード変換は行っていないようです。
C:\>jirb -Ks --noreadline irb(main):001:0> '日本語' => "日本語" irb(main):002:0> java.lang.String.new('日本語').to_s => "・\275・\275{・\275・\275"
WindowsのコマンドプロンプトではSJISを使っているので、当然ながら -K オプションでUTF-8を宣言すると、文字化けしてしまします。
C:\>jirb -Ku --noreadline irb(main):001:0> '日本語' => "\223?{語" irb(main):002:0> java.lang.String.new('日本語').to_s => "・ス・ス{・ス・ス"
Windowsのコマンドプロンプトでchcp 65001を実行すると、UTF-8が使えるらしいのですが、なぜか僕の環境ではjirbを実行できませんでした。
Windowsのコマンドプロンプトをあきらめ、jirb_swingを使えば、文字化けが無くなります。
C:\>jirb_swing -Ku
irb(main):001:0> '日本語' => "日本語" irb(main):002:0> java.lang.String.new('日本語').to_s => "日本語"
SequelはJDBC以外ならいい
今度はSequelを試してみます。まずは、PostgreSQLにpostgres-prでつないでみます。
require 'rubygems' require 'sequel' DB = Sequel.postgres(:database => 'postgres', :user => 'postgres', :password => 'postgres') DB[:user_data].all.each do |user_data| puts "#{user_data[:user_id]} #{user_data[:user_account].kconv(Kconv::SJIS, Kconv::UTF8)}" end
モデルを作らなくても使えるのが、いいですね。
ActiveRecordの時と同様、kconvによるSJIS変換は無視してください。
JDBCだとこんな感じ。
require 'rubygems' require 'sequel' DB = Sequel.jdbc(:uri => 'jdbc:postgresql:postgres', :user => 'postgres', :password => 'postgres') Sequel::JDBC.load_driver('org.postgresql.Driver') DB["SELECT * FROM user_data"].all.each do |user_data| puts "#{user_data[:user_id]} #{user_data[:user_account].kconv(Kconv::SJIS, Kconv::UTF8)}" end; nil
JDBCドライバの登録が美しくありません。どなたか他に方法がありましたら教えてください。
また、JDBCの場合、シンボルによる表指定ができません。DB["SELECT * FROM user_data"].allは実行できても、DB[:user_data].allはエラーになります。
DB["SELECT * FROM user_data"].all # success DB[:user_data].all # fail
なぜかというと、JDBC接続時にDB[:user_data].allによって作られるSQL文が SELECT * FROM \"USER_DATA\" になってしまうからなんですよね。PostgreSQLでは表名の大文字と小文字を区別し、デフォルトでは小文字なので、USER_DATAなんて表はないということになってしまうのです。
sequel_core/sql.rbに以下のような記述がありました。
# This method quotes the given name with the SQL standard double quote. # It uppercases the name given to conform with the SQL standard. This # should be overridden by subclasses to provide quoting not matching the # SQL standard, such as backtick (used by MySQL and SQLite), or where # lowercase is the default for unquoted identifiers (PostgreSQL). # # If you are using a database such as Oracle that defaults to uppercase # but you are using lower case identifiers, you should override this # method to not upcase the name for those identifiers. def quoted_identifier(name) "\"#{name.to_s.upcase}\"" end
これが原因らしいぞ。デフォルトで大文字にしちゃうわけね。adapters/postgres.rbではquoted_identifierをオーバーライドしてupcaseを消し去るのでDB[:user_data].allはエラーにならないが、JDBCだと大文字の表を探しちゃうわけか。
というわけで、回避策としては、シンボルを使わないか、Sequel::JDBC::Datasetを書き換えるかです。後者だと、以下のようになります。
require 'rubygems' require 'sequel' module Sequel module JDBC class Dataset < Sequel::Dataset def quoted_identifier(name) "\"#{name.to_s}\"" end end end end DB = Sequel.jdbc(:uri => 'jdbc:postgresql:postgres', :user => 'postgres', :password => 'postgres') Sequel::JDBC.load_driver('org.postgresql.Driver') DB[:user_data].all.each do |user_data| puts "#{user_data[:user_id]} #{user_data[:user_account].kconv(Kconv::SJIS, Kconv::UTF8)}" end
他にも罠がありそうで、JDBC接続でSequelを使うのは危険な感じがします。
ActiveRecordを試してみた
PostgreSQLにpostgres-prでつないでみる。
require 'rubygems' require 'activerecord' ActiveRecord::Base.establish_connection( :adapter => "postgresql", :host => "localhost", :database => "postgres", :username => "postgres", :password => "postgres", :encoding => "SJIS" ) class UserData < ActiveRecord::Base set_table_name "user_data" set_primary_key "user_id" end UserData.find(:all).each do |user_data| puts "#{user_data.user_id} #{user_data.user_account}" end
JDBCだとこんな感じ。
require 'rubygems' require 'activerecord' require 'kconv' ActiveRecord::Base.establish_connection( :adapter => "jdbcpostgresql", :host => "localhost", :database => "postgres", :username => "postgres", :password => "postgres" ) class UserData < ActiveRecord::Base set_table_name "user_data" set_primary_key "user_id" end UserData.find(:all).each do |user_data| puts "#{user_data.user_id} #{user_data.user_account.kconv(Kconv::SJIS, Kconv::UTF8)}" end
Windowsでコンソールに出力させている関係上、kconvでSJISへの変換が入っているが、実システムではUTF-8を使うので不要だね。