iTunes に自動でトラックを追加する PowerShell スクリプト [PowerShell]
パソコンでラジオ番組を録音していて、特定のフォルダに「番組名 YYYY-mm-dd.mp3」というファイル名で保存されるようになっている。これを勝手に iTunes に登録してくれるようにしたい。iTunes は Windows Media Player のような「フォルダの監視」機能がないので何かスクリプトを書いてやらないといけない。タスクスケジューラに登録して毎日実行するようにすればよさそうだ。
というのを PowerShell でやるとこう。楽だなー。
$app = new-object -com iTunes.Application
ls (date -uformat *%Y-%m-%d.mp3) |
foreach { $app.LibraryPlaylist.AddFile($_.FullName) }
$app.Quit()
O'Reilly から PowerShell 本 [PowerShell]
O'Reilly からも PowerShell 本 [1] が出るようだ。結構楽しみ。この人も PowerShell 開発者なんだな。
ところで(この本がどうだかはわからないけど)米 Microsoft の技術者は技術的な記事や書籍をとてもユーモラスに書く人が多いような印象がある。そういう教育でも受けてるのかと思うくらい。最近買った PowerShell in Action の Bruce Payette もそうだったし、元 Microsoft の Joel Spolsky もそうだし、MSDN のコラム(例えば Dr. GUI のシリーズ)とか、あと最近複数の箇所で紹介されていたんだけど [2] の記事とか。
[1] http://www.oreilly.com/catalog/9780596528492/
[2] http://msdn.microsoft.com/library/ja/jpdnvc60/htm/MemLeaks.asp?frame=true
パイプラインにおける PowerShell の関数 [PowerShell]
PowerShell インアクションを読むと思わずのけぞるような箇所にしばしば出会うが、関数定義内に begin と end が書けるというのもそのひとつだった。
function sum {
begin { $result = 0 }
process { $result += $_ }
end { $result }
}
begin というのは awk の BEGIN で end は awk の END である。上記の関数はパイプライン上で以下のように使える。
PS D:\ps1> 1..10 | sum 55
これを思いついて実際に言語に載せるというのはすごいなあ。
これもそうだけど PowerShell はパイプラインという概念を言語の中心にすえているところがあって、そのため(制御構文は普通に一通りあるのだが)何でもパイプライン上に置きたくなるようなところがある。おそらく Ruby でメソッドチェインをつなげたり Haskell でポイントフリースタイルを多用したくなるようなのと似た感覚なのだと思う。
PowerShell の見えないアキュムレータ [PowerShell]
ある数値の配列が与えられたときにそこから重複要素を省いた配列を得る関数を PowerShell で書くとこうなる。(どう書く?orgに投稿したもの [1] を少し改変した)
function uniq ($xs) {
$seen = @{}
foreach ($x in $xs) {
if (-not $seen[$x]) { $x }
$seen[$x] = $true
}
}
ここで既出の要素を判定しているのはともかくとして結果を格納する配列はどうした?と思うかもしれない。でもこれでいいのだ。
4行目の if の { $x } だけからなる節は変数 $x の内容を評価する。PowerShell では値がある文の値は何も言わなければ出力されるので以下のようになる。
PS D:\ps1> uniq 3,1,4,1,5,9,2,6,5,3,5,8,9,7,9 3 1 4 5 9 2 6 8 7
じゃあこれは要素をユニーク化した配列内容を表示する関数であって配列を返すものではないじゃないか。それも違う。以下の例を見てほしい。
PS D:\ps1> $xs = uniq 3,1,4,1,5,9,2,6,5,3,5,8,9,7,9 PS D:\ps1> $xs.length 9 PS D:\ps1> $xs.gettype() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array
$xs にはちゃんと要素がユニークになった配列が入っている。 PowerShell では関数やスクリプトの出力はパイプラインの中にあればオブジェクトのストリームと解釈されるが、変数への代入のような文脈では自動的にアキュムレータを作ってそこに出力を掻き集めてくれるのだ。
カレントディレクトリ以下のファイル・ディレクトリを再帰的に集めて配列に入れたければ以下のように書く。(実際には Get-ChildItem コマンドレットに -recurse オプションが使えるので自分で作る必要はない)
function walk-dir ($path) {
dir $path | foreach {
if ($_ -is [System.IO.DirectoryInfo]) { walk-dir $path/$_ }
$_
}
}
$fs = walk-dir .
上記の4行目のような文はいわば「無標の yield」(勝手に名前を付けた)であって「 yield $_ 」のようなものだと思うとよいのかもしれない。Python などの yield との違いは常にコレクション全体を生成するのであってジェネレータのように計算が途中で中断された状態ではないという点だ(つまり無限リストは出来ない)。
[1] http://ja.doukaku.org/comment/1664/
PowerShell インアクション [PowerShell]
書店で PowerShell 開発者の一人の手による「Windows PowerShell インアクション」の日本語訳が置いてあったので衝動買いした。急いでかなりの部分読んだけど、読めば読むほどすごいシェル言語だと思う。これはもう偉業といっても差し支えないのではないか。
PowerShell の変数のスコープ [PowerShell]
http://blogs.msdn.com/powershell/archive/2007/04/14/controlling-the-scope-of-variables.aspx
Set-Variable コマンドレットで -Scope オプションを正数にすると関数を呼び出した側のスコープにある変数を変更できるという話。これは Tcl の uplevel とか upvar と同じような考え方だ。これはクロージャのように定義時に決まるのではなくて呼び出し時に決まるのである種の動的スコープなんだけど Tcl 以外でこういう仕様の言語は初めて見た。
PowerShell でブラウザのスクリーンキャプチャをとる [PowerShell]
IE でウェブサイトを開いてそのスクリーンショットを撮るスクリプトを書いてみた。
Param([String]$url, [String]$filename, [String]$format="png")
if (!$url -or !$filename) { return }
$ie = new-object -com InternetExplorer.Application
# URL を開く
$ie.Navigate($url)
# ページをロードし終わるまで待つ
while ($ie.Busy) {sleep -milliseconds 10}
# バー関連はすべて消して IE を可視化
$ie.StatusBar = $false
$ie.ToolBar = $false
$ie.MenuBar = $false
$ie.AddressBar = $false
$ie.Visible = $true
# IE のウィンドウを前面に持ってくる
$wsh = new-object -com WScript.Shell
$wsh.AppActivate("Explorer")
# スクリーンキャプチャ
$bitmap = new-object Drawing.Bitmap($ie.Width, $ie.Height)
$graphics = [Drawing.Graphics]::FromImage($bitmap)
$graphics.CopyFromScreen($ie.Left, $ie.Top, 0, 0, $bitmap.Size)
$bitmap.Save($filename, $format)
$ie.Quit()
これを cap.ps1 のような名前で保存して以下のように使う。
PS G:\> .\cap.ps1 http://www.yahoo.co.jp/ test.gif gif
画面サイズを調整したい場合は $ie.Width と $ie.Height に値を代入してあげれば良いと思う。
Windows PowerShell 雑感 [PowerShell]
「Windows PowerShell 宣言!」 [1] という本を買った。ざっと読んだりして感想。
1. コマンド名表記の慣習はなんか変
例えばエイリアスを取得するためのコマンドはカノニカルには Get-Alias と書く。こんな書き方他の言語ではあまり見たことない。キャメルにするならハイフンいらないしハイフン挟むならキャメルいらないと思う。幸い PowerShell では大文字小文字は区別されないので get-alias とも書ける。こうしたい。
2. プロバイダという概念の導入
UNIX なんかでは「ファイルではないようなものもファイル扱いする」という形である種の抽象化を行っていて、それはかなり成功している。PowerShell では「ドライブではないようなものもドライブ扱いする」というアイデアを持ち込んでいて、そのドライブのようなものがプロバイダと呼ばれる。Cドライブは C: で、環境変数へのアクセスを提供するプロバイダは Env: で、エイリアスの一覧は Alias: で、といった感じ。
PS C:\Documents and Settings\ether> cd env: PS Env:\> dir path Name Value ---- ----- Path C:\SMLSharp\bin;C:\Tcl\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:... PS Env:\>
これはなかなか面白くて成功しそうなアプローチだと思う。MS-DOS 時代からの名残の厄介者だった「ドライブレター」という存在に新たな意味を与えているところがポイントだと思うけど、これは良いことなのか悪いことなのかよくわからない。独自のプロバイダを作ることも可能なのかな。
3. 構文の飛躍
普通にシェルとして使う分には「スペース区切りで最初がコマンドで後が引数」というシェルらしい構文に見えるのだが .NET ライブラリを使うようなスクリプトになるとコードが普通のオブジェクト指向言語っぽい構文で満たされるようになって、とても同じ延長線上にあるような言語には見えなくなる。
コマンドラインヒストリの最初の5行を表示:
history | select -first 5
ウェブサイトの内容を取得:
$url = "http://www.google.com"
$client = new-object net.webclient
$client.downloadfile($url, "google.html")
構文解析のされかたについていまいち直感が沸かない。いろいろ試してみないと。
4. 多言語連携が面白そう
「Windows PowerShell 宣言!」という本は IronPython との連携とか C# や VB との連携とか WSH との連携とかをかなり取り上げているのが面白い。PowerShell から VisualBasic のコードをオンザフライでコンパイルして実行するとか、へ~、と思った(これは PowerShell というより .NET として提供している機能なのかもしれないけど)。 .NET は言語混ぜ合わせてごちゃごちゃ遊びたい人には垂涎の環境なのかもしれないとか思えてきた。
今後の目標: いろいろいじって遊びたい
[1] http://www.sbcr.jp/books/products/detail.asp?sku=4797340273
Windows PowerShell [PowerShell]
http://www.atmarkit.co.jp/fdotnet/special/powershell02/powershell02_01.html
Windowsの次世代シェルではコマンドの実行結果が行のストリームではなく、「オブジェクト」になる。で、パイプの中をオブジェクトが流れるという話。
これはなかなかかっこいいな。