Developer's Blog

よくわからん PowerShell とりあえず使おう!

こんにちは、開発担当の松本です。

シェルスクリプトは書けるがバッチファイルは書けない人間が、Windows 7 から標準搭載された Windows PowerShell を始めて使ってみました。

シェルスクリプトでよく書きそうなものを PowerShell で書くとどうなるのか。また、PowerShell でどんなことできるのかを簡単に紹介します。

とりあえずの例

「とりあえずこんなことができるんだ!」という例です。 コマンドプロンプト上で次のコマンドを実行させると、タスクバー上にメモ帳がピン止めされます。

powershell -Command "&{$shell = New-Object -ComObject Shell.Application; $path = 'C:\Windows\notepad.exe'; $dir = $shell.NameSpace((Split-Path $path)); $link = $dir.ParseName((Split-Path -Leaf $path)); $link.Verbs() | %{ if($_.Name.ToString() -eq 'Pin to Tas&kbar'){ $_.DoIt() } }}" 

いろいろな使いかた

ここでは、シェルスクリプトでよく書きそうなものが PowerShell ならこうなる! という例をいろいろ紹介します。

紹介している書きかたより、もっとよい方法があるかもしれませんが、そのへんはご容赦を。

find -iname ‘*.txt’

まずは小手調べから。今だと、ls **/*.txt のように書けたりもしますね。これは PowerShell ではこのように書けます。

PS> ls -Recurse -Include '*.txt' 

ちなみに、-Recurse を付けてグロブしたいときには -Include を付ける必要があるようです。

find -iname ‘*.txt’ -print0 | xargs -0 rm

これも、xargsを使わずに find -iname ‘*.txt’ -delete でもよいですし、今だと、rm **/*.txt のように書けたりします。PowerShell では次のようになります。

PS> ls -Recurse -Include '*.txt' | rm 

sed -re ‘s/(\w+)/[\1]/g’ foo.txt

正規表現を使った文字列置換の例です。PowerShell では文字列に -replace や -match で正規表現の置換やテストができます。 …がデフォルトで大文字小文字の区別をしないので、区別させたいときには c を付けたバージョンを使う必要があります。

PS> cat foo.txt | %{$a = $_ -creplace '(\w+)','[$1]'; "$a" } 

ここで、% は ForEach-Object コマンドレットのエイリアスで、これは入力の各要素に対してブロック {…} を適用します。 そして、ブロックに与えられた各要素は、ブロック内では $_ で取り扱うことができます。

つまりこの例では、入力ファイルの各行に対して文字列を置換してから出力させる処理を行っています。

ちなみにエイリアスの一覧は alias で確認できます。ls も Get-ChildItem へのエイリアスですし、rm も Remove-Item へのエイリアスだったりします。

for i in *.json ; do cp $i $i.bak ; done

単純な for 文の例ですが、PowerShell ではパイプを使って書けます。

PS> ls '*.json' | cp -path {$_.Name} -Destination {$_.Name + ".bak"} 

なお余談ですが、この記事を書くにあたって数名からシェルスクリプトのワンライナーを募ったのですが、とある Plan 9 使いは次のようなものをくれました。

> ls -1 *.txt | sed 's/.*/cp & &.bak/' | sh 

sort foobar.txt | uniq -c | sort -rn | head -n 10

おなじみアクセスログとか見るときに頻度のトップ 10 を取るワンライナー。PowerShell では次のようになります。

PS> cat foobar.txt | group -NoElement | sort -Property count -Descending | select -First 10 

group はグルーピングのためのコマンドレット (のエイリアス) で、select は Unix 的に言えば行や列を選択できるコマンドレット (のエイリアス) です。 こうして見るとだいたい同じように見えなくもないですね。

sort -t \t -k 3 foobar.tsv | awk -F ‘\t’ ‘{print $1, $3, $2}’

最後にあまりよい例ではないですが、タブ区切りテキストファイルを扱う例です。 PowerShell だと Import-Csv で安全に CSV などを処理できるようになります。

PS> Import-Csv -Delimiter "`t" foobar.tsv -Header id,x,y,z | sort -Property y | select id,y,x 

Jenkins のビルドをさせる

PowerShell では.Net Framework のオブジェクトを利用できますので、HttpWebRequest を利用して Jenkins のリモートアクセス機能にアクセスしてジョブを実行させるのも割合簡単にできます。

PS> powershell -Command "&{([System.Net.HttpWebRequest]::Create('http://127.0.0.1:8080/job/test1/build?token=foobar')).GetResponse()}" IsMutuallyAuthenticated : False Cookies : {} Headers : {X-Hudson-Theme, X-Hudson, X-Jenkins, X-Jenkins-Session...} ContentLength : -1 ContentEncoding : ContentType : text/html;charset=UTF-8 CharacterSet : UTF-8 Server : Winstone Servlet Engine v0.9.10 LastModified : 2012/10/05 17:39:26 StatusCode : OK StatusDescription : OK ProtocolVersion : 1.1 ResponseUri : http://127.0.0.1:8080/job/test1/ Method : GET IsFromCache : False 

この機能と、以前に紹介した Mercurial のフックとを組み合わせると、コミットした瞬間にジョブを開始させることができます。これは、次の 2 行を .hgrc に書いておくだけです。

[hooks] commit.jenkins = powershell -Command "&{([System.Net.HttpWebRequest]::Create('http://127.0.0.1:8080/job/test1/build?token=foobar')).GetResponse()}" 

Redmine タスクの時間を記録する

HttpWebRequest では POST もがんばればできますので、Redmine REST API を利用して、指定したタスクの時間を記録させるようにすることもできます。

$hxr = [System.Net.HttpWebRequest]::Create("http://127.0.0.1:8080/time_entries.xml") $hxr.Method = "POST" $hxr.ContentType = "application/xml" $hxr.Headers["X-Redmine-API-Key"] = "bfdbdaxxxxxxxxxx7231574dbxxxxxxxxxxcf82" $contents = '<?xml version="1.0" encoding="UTF-8"?><time_entry><issue_id>128</issue_id><hours>1.5</hours><activity_id>8</activity_id></time_entry>'.ToCharArray() $hxr.ContentLength = $contents.Length $stream = $hxr.GetRequestStream() $stream.Write($contents, 0, $contents.Length) $stream.close() $hxr.close() 

ここまでくるとかなり面倒なので、cURL を利用したほうが楽です。

> curl -v -H "Content-Type: application/xml" -X POST --data '<?xml version="1.0" encoding="UTF-8"?><time_entry><issue_id>570</issue_id><hours>0.25</hours><activity_id>8</activity_id></time_entry>' -H "X-Redmine-API-Key: bfdbdaxxxxxxxxxx7231574dbxxxxxxxxxxcf82" http://127.0.0.1:8080/time_entries.xml 

COM オブジェクトを利用する

一番最初に紹介したタスクバー上にメモ帳がピン止めされる例です。このように COM オブジェクトを利用することもできます。

$shell = New-Object -ComObject Shell.Application $path = "C:\Windows\notepad.exe" $dir = $shell.NameSpace((Split-Path $path)) $link = $dir.ParseName((Split-Path -Leaf $path)) $link.Verbs() | %{ if($_.Name.ToString() -eq "Pin to Tas&kbar"){ $_.DoIt() } } 

おわりに

シェルスクリプトでよく書きそうなものを PowerShell で書くとどうなるのか、などを紹介しました。

シェルスクリプトに似ているようで全然違うという感じですが、回りの環境が全部 Windows 7 というような状況であれば、かなり使いでのあるスクリプトだなあと感じました。

ヘルプも充実しているので Windows 上での開発者はぜひ一度使ってみてください。

ソーシャルアカウント

Sleipnir の Facebook ページでは、ユーザーの方たちとのコミュニケーションや最新情報の投稿などを行なっています。よろしければいいね!してください!

フェンリルの開発者アカウントでは、フェンリルプロダクトの最新開発情報などをつぶやいています。よろしければフォローしてください!

Copyright © 2019 Fenrir Inc. All rights reserved.