PATH以下のファイルに対する操作

path-childitemというCmdletを使う。

MSH> get-childitem C:\TEMP\ -Include *.cs -Recurse


   Directory: FileSystem::C:\TEMP\BLL\Properties


Mode    LastWriteTime            Length Name
----    -------------            ------ ----
-a---   8 10 16:37                 1637 AssemblyInfo.cs


   Directory: FileSystem::C:\TEMP\BLL


Mode    LastWriteTime            Length Name
----    -------------            ------ ----
-a---   8 11 10:30                 2044 AContext.cs
-a---   8 11 10:30                 2619 CManager.cs
-a---   8 10 21:54                 3580 Cryptograph.cs
-a---   8 10 14:25                  531 Localization.cs
-a---   8 10 15:47                  487 Logging.cs
-a---   8 10 20:24                  574 Login.cs
-a---   8 10 14:41                  438 PCounter.cs
-a---   8 10 15:47                  916 RManager.cs
-a---   8 10 15:47                  556 SConfiguration.cs
-a---   8 10 14:06                  521 Usage.cs
-a---   8 10 14:34                  394 UInfo.cs
-a---   8 10 14:42                  402 UPreference.cs
-a---   8 10 14:31                  381 Utility.cs


ちょっとこれじゃ使いにくいので、フルパスの文字列を出力するように整形する。

MSH> get-childitem C:\TEMP -Include *.cs -Recurse|foreach{combi
ne-path $_.Directory $_.Name}
C:\TEMP\BLL\Properties\AssemblyInfo.cs
C:\TEMP\BLL\Properties\AssemblyInfo.cs
C:\TEMP\BLL\ApplicationContext.cs
C:\TEMP\BLL\CacheManager.cs
C:\TEMP\BLL\Cryptograph.cs
C:\TEMP\BLL\Localization.cs
C:\TEMP\BLL\Logging.cs
C:\TEMP\BLL\Login.cs
C:\TEMP\BLL\PerformanceCounter.cs
C:\TEMP\BLL\RoleManager.cs
C:\TEMP\BLL\SystemConfiguration.cs
C:\TEMP\BLL\Usage.cs
C:\TEMP\BLL\UserInfo.cs
C:\TEMP\BLL\UserPreference.cs
C:\TEMP\BLL\Utility.cs
MSH>

コマンドの実行結果が、どんなプロパティを持ったオブジェクトなのか見たい、という時は、format-listというCmdletを使うと見やすく整形してくれる。

MSH> $a=get-childitem C:\TEMP -Include *.cs -Recurse
MSH> $a[0]|format-list


   Directory: FileSystem::C:\TEMP\BLL\Properties



Name           : AssemblyInfo.cs
Length         : 1637
CreationTime   : 2005/08/10 11:34:37
LastWriteTime  : 2005/08/10 16:37:08
LastAccessTime : 2005/08/11 10:30:23
VersionInfo    :

で、パイプラインで必要なプロパティだけに間引きたい時には、select-objectを使う。

MSH> get-childitem C:\TEMP -Include *.cs -Recurse|select-object
 Name

Name
----
AssemblyInfo.cs
AContext.cs
CManager.cs
Cryptograph.cs
Localization.cs
Logging.cs
Login.cs
PCounter.cs
RManager.cs
SConfiguration.cs
Usage.cs
UInfo.cs
UPreference.cs
Utility.cs

コマンドの調べ方

.NET Frameworkに関して調べたい時は、MSDNのリファレンスなり、TIPSのサイトをぐぐるなり、何とでもなる。
Monad固有のCmdletはどうやって調べるかという話。

get-commandというCmdletを使うと、どんなCmdletがあるか、簡単な使い方が分かる。

MSH> get-command get-help

Command Type    Name                      Definition
------------    ----                      ----------
Cmdlet          get-help                  get-help [[-Name] String] [-Catego...

Definitionが見たいんだけど、というような時は、select-objectを使うと、パイプで渡されたレコードのうち、選択したプロパティだけを返してくれる。SQLの射影みたいな操作。

MSH> get-command get-help|select-object Definition

Definition
                  • -
get-help [[-Name] String] [-Category String[]] [-Component String] [-Functio...

または、format-listを使うと、下記のように1レコードを複数行に表示してくれる。

MSH> get-command get-help|format-list


Name          : get-help
CommandType   : Cmdlet
Definition    : get-help [[-Name] String] [-Category String[]] [-Component Stri
               ng] [-Functionality String] [-Role String] [-Verbose] [-Debug]
               [-ErrorAction ActionPreference] [-ErrorVariable String] [-OutVa
               riable String] [-OutBuffer Int32]

Path          :
AssemblyInfo  :
DLL           : C:\Program Files\Microsoft Command Shell\System.Management.Auto
               mation.DLL
HelpFile      : System.Management.Automation.dll-Help.xml
ParameterSets : {__AllParameterSets}
Type          : System.Management.Automation.Commands.GetHelpCommand
Verb          : get
Noun          : help

詳しい説明が表示されるのは、get-helpというCmdlet。UNIXでいうmanみたいな感じ。
get-help [Cmdlet]というように使う。

ループの簡単な書き方

You normally would use a counted for loop:

MSH:19 C:\temp\monad > for($x = 0; $x -lt 5; $x++) { do-something }

But here's a neat little trick to save some typing, if you don't care which iteration of the loop you're in:

MSH:19 C:\temp\monad > 1..5 | foreach { do-something }

This is a bloated and slow way to do a for loop, though, so don't use it in scripts.

forループを使うより、配列をパイプに渡してforeachを使ったほうがシンプルだし速い、という事かな?

例えば、こんな感じでIrvineなどに頼らずに自力でダウンロードするとか。

MSH:19 C:\temp\monad > $url="http://www.hoge.com/aaa/bbb/ccc/"
MSH:19 C:\temp\monad > 1..50|foreach {$f=[String]::Format("{0:00}.jpg", $_); $c.DownloadFile($url + $f, $f)}

こういうのをモヒカン族って言うのじゃろか?

プロンプトの変更

http://www.leeholmes.com/blog/default,date,2005-06-12.aspx
を参考に、プロンプトを変更してみた。

一目瞭然だと思うが、無理やり説明すると。
カレントディレクトリだけの名前を取得するのにswitch文で正規表現を使った。
ドライブ直下の時はそれだとうまく行かないので、その時はdefaultで対処。
文字列のフォーマッティングはStringのFormat関数を使った。

MSH>function prompt {
>> switch -regex ((get-location).path) {
>> "(?<current>[^\\]+$)" {
>> $c = $matches.current
>> }
>> default {
>> $c = $_
>> }
>> }
>> [String]::Format("[{0}] {1} >", $c, (get-history -count 1).id)
>> }
>>
[C:\] 0 >cd windows
[WINDOWS] 1 >cd system32
[system32] 2 >

サンプル:ファイルのSHA1ハッシュを計算する。

MSH> function GetSha1 {
>> $path = $args[0]
>> $bytes = [System.IO.File]::ReadAllBytes($path)
>> $sha1 = new-object System.Security.Cryptography.SHA1Managed
>> $hash = $sha1.ComputeHash($bytes)
>> $retval = ""
>> foreach ($b in $hash) {$retval += $b.ToString("x2")}
>> $retval
>> }
>>
MSH> GetSha1("C:\TEMP\Console1\bin\Debug\Console1.exe")
2c0371c737f9785fc6db4f35ba0973eaa29e1fd0

従来の環境では、ライブラリのインターフェースとコマンドラインのインターフェースが著しく異なっていたために、コマンドラインのプログラムという形でライブラリをラップする必要があった。
このため、コマンドを作らないとライブラリが呼び出せなかったし、ライブラリとは異なるコマンドの使い方を別途覚える必要が生じた。

Monad Shellの場合、いきなりシェルから.NET Frameworkが使える。
コンソールアプリを作る時も、シェルスクリプトを書くときも、同じように.NET Frameworkを使う。
すばらしい。
この環境から始めた人は、UNIXの混沌には耐えられないだろうな。

.NET関連の操作

Microsoft Command Shell(Monad Shell)で遊んだ続き。
.NET Frameworkがそのまま使えるってのを試してみた。
かなり面白い。rubyでいう所のirbみたい。気に入りました。

サンプル:ファイルのダウンロード

New-ObjectというCmdletを使うとインスタンスを作成できる。

MSH> $c = new-object System.Net.WebClient
MSH> $c.DownloadFile("http://www.google.com/", "c:/temp.html")

サンプル:メール送信

これも同様。

MSH> $cl = new-object System.Net.Mail.SmtpClient
MSH> $cl.Host = "smtp"
MSH> $msg = new-object System.Net.Mail.MailMessage
MSH> $from = new-object System.Net.Mail.MailAddress("hoge@fuga")
MSH> $to = $from
MSH> $msg = new-object System.Net.Mail.MailMessage($from, $to)
MSH> $msg.Body = "aaaaa"
MSH> $msg.Subject = "test subject"
MSH> $cl.Send($msg)

staticなMethodを呼ぶ

[クラス名]::メソッド名()って感じで呼び出せる。

MSH> [System.Enum]::GetValues([System.IO.FileAccess])
Read
Write
ReadWrite
MSH>

Enumにアクセス

これもStatic Methodと同様で、[クラス名]::Enum名。
ちなみにプロパティの場合も同じ感じ。

MSH> $out = new-object System.IO.FileStream("C:/temp.txt",
>> [System.IO.FileMode]::Create,
>> [System.IO.FileAccess]::Write)
>>
MSH> $c = new-object System.Net.WebClient
MSH> $a = $c.DownloadData("http://www.yahoo.co.jp/")
MSH> $out.Write($a, 0, $a.Length)
MSH> $out.Close()
MSH>

アセンブリのLoad

[Reflection.Assembly]::LoadWithPartialName("System.Web")

Cmdletを試す

引き続きMicrosoft Command Shell(Monad Shell)で遊ぶ。
sh等の既存のシェルで言う所のbuiltin Commandに該当するのがCmdletだ。
verb-nounという形式に命名法が統一されているのに感心した。
もちろん、普通使いたいコマンドにはaliasが切ってあるので、cdとかlsできる。

まぁ、まずは触ってみようということで、とりあえずレジストリをいじってみた。

まずは目的の位置に移動。

MSHのプロンプトを起動した後、まずは移動して、Get-Itemで現在の内容を見る。

MSH> cd hkcu:\software\microsoft\windows\currentversion
MSH> get-item run


   Hive: Registry::HKEY_CURRENT_USER\software\microsoft\windows\currentversion

SKC  VC Name                           Property
---  -- ----                           --------
 0   1 run                            {ctfmon.exe}

New-Propertyで新しい項目を登録する。

Windowsの起動時に電卓が立ち上がるようにしてみた。

MSH> new-property -path run -property "calc.exe" -value "C:\windows\system32\cal
c.exe"


MshPath       : Registry::HKEY_CURRENT_USER\software\microsoft\windows\currentv
               ersion\run
MshParentPath : Registry::HKEY_CURRENT_USER\software\microsoft\windows\currentv
               ersion
MshChildName  : run
MshDrive      : HKCU
MshProvider   : System.Management.Automation.ProviderInfo
calc.exe      : C:\windows\system32\calc.exe

値が変わって、calc.exeが増えているのがわかる。

MSH> get-item run


   Hive: Registry::HKEY_CURRENT_USER\software\microsoft\windows\currentversion

SKC  VC Name                           Property
---  -- ----                           --------
 0   2 run                            {ctfmon.exe, calc.exe}

実際に登録されているかどうか、Get-Propertyで確認する。

MSH> get-property -path run -property calc.exe


MshPath       : Registry::HKEY_CURRENT_USER\software\microsoft\windows\currentv
               ersion\run
MshParentPath : Registry::HKEY_CURRENT_USER\software\microsoft\windows\currentv
               ersion
MshChildName  : run
MshDrive      : HKCU
MshProvider   : System.Management.Automation.ProviderInfo
calc.exe      : C:\windows\system32\calc.exe

はい、確かにcalc.exeがレジストリに登録されています。

Remove-Propertyで削除する。

本当に起動時に電卓が立ち上がってもジャマなので。

MSH> remove-property -path run -property calc.exe
MSH> get-item run


   Hive: Registry::HKEY_CURRENT_USER\software\microsoft\windows\currentversion

SKC  VC Name                           Property
---  -- ----                           --------
 0   1 run                            {ctfmon.exe}

.NETのオブジェクトも扱えるようだが、ちょっとまだ良くやり方が分からない。
っていうか、本当にドキュメントが無い。