PowerShell Cookbookが良すぎる

先日『PowerShellポケットリファレンス』をとりあえずは読了して、『PowerShell Cookbook』(3rd edition)を読み始めたが、
改めて実用的なサンプルが盛りだくさんだと感動している次第。1000ページもあるのにどのレシピも役に立つものばかりだ。
例えば、$PSDefaultParameterValuesの具体的な使い方。ハッシュでまとめて指定したり、スクリプトブロックで動的に指定したり、共通パラメータCredentialの既定値をワイルドカードで一括指定したり。
情報がAbout Topicsと少数のWebページくらいしかないようなことでも、とてもコンパクトに要点を押さえて書かれているので貴重な書籍だと思う。
ダウンロードできるサンプルコードも豊富で良質だ。当分はCookbook先生から学んでいこうと思う。

3rd editionはPowerShell 3.0までしかカバーしていないので、来年あたりに4th出ないかなと気になっている。それにしても3rdは初版の2倍近くのページ数。
次は何ページになるんだろう。紙の本だと本棚のスペースをとるのでKindle版一択だなあ。Kindle版だと半額近いし。洋書はKindle化されてるものが多くてしかも安くていいな。

4.0の情報を補完する意味でも、Cookbookと並行してAbout Topicsも少しずつ読んでいきたい。

配列のflatten

配列において

((1,2), (3,4))→(1, 2, 3, 4)

とflattenするメソッドを追加してみた。

<?xml version="1.0" encoding="utf-8" ?> 
<Types>  
  <Type> 
    <Name>System.Array</Name> 
    <Members> 
      <ScriptMethod> 
        <Name>Flatten</Name> 
        <Script> 
            function _flatten
            {
                $args[0] | % {
                    if($_ -is 'Array')
                    {
                        _flatten $_
                    }
                    else
                    {
                        $_
                    }
                }
            }
            _flatten $this
        </Script> 
      </ScriptMethod> 
    </Members> 
  </Type> 
</Types>

型データディレクトリ$typedirを適当に作成し、上の内容をSystem.Array.ps1xmlで保存し、プロファイルで

Update-Typedata -PrependPath $typedir\System.Array.ps1xml

とすれば使える。

サンプル

> ((1,2),(3,4))[1]
3
4

> ((1,2),(3,4)).Flatten()[1]
2

vimからpowershell

set shell=C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe

としておくと色々うれしい。

:r !Get-Date

で日時挿入。

:r !ls $doc -directory -recurse *hoge* | % fullname

でリンク集完成。
(パス名上でgfでそのファイルを開く。フルパスでなくてもvimのpath変数から名前解決してくれる。空白文字を含む時はVで行選択してgfでOK。)

:sh

PowerShell起動。

:!{command}

PowerShellのコマンド実行。

1点残念なのは、PowerShellコマンドに入力を渡す時だ。

:w !{command}

とすると

発生場所 行:1 文字:5
+ {command} <C:/Users/foo/AppData/Local/Temp/VIi890A.tmp
+           ~
演算子 '<' は、今後の使用のために予約されています。
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordEx
   ception
    + FullyQualifiedErrorId : RedirectionNotSupported

と怒られる。一時ファイルを作って'<'で入力しているらしい。入力を渡すvimスクリプトを書けば済む話かもしれないが。

CubePDF用の一括リネームスクリプト

CubePDFではjpgで保存すると既定ではdocument-001.jpg, document-002.jpg, ...という連番ファイル名になる。
これを変更するにはファイル名入力フォームをマウスでクリックしてファイル名を入力すれば良いのだが、
キーボードだけで済ませたいのでスクリプトを書いてみた。ついでに日時も入れられるようにした。


ps1ファイル

<#
.SYNOPSIS
番号違いのファイルを一括リネーム
.DESCRIPTION
ファイル名末尾に通し番号のついたファイル hoge001.foo のフルパスを引数に与えると、
同じディレクトリの番号違いのファイル hoge002.foo, hoge003.foo, ...も一括して
fuga001.foo, fuga002.foo, ... にリネームする。(fugaはユーザー入力)

通し番号のついていないファイル hoge.foo が与えられると、
単にfuga.fooにリネームされる。

また、ファイル名先頭に日時を入れるか選択できる。
#>
param([string]$file)

$useTimestamp = (Read-Host "ファイル名に日時を入れますか?(y/n)")
if($useTimestamp -eq 'y')
{
	$ts = Get-Date -f "yyyyMMdd hh-mm-dd"
}
else
{
	$ts = ''
}

$title = (Read-Host "タイトルを入力してください")

$parent = Split-Path $file -parent
$leaf = Split-Path $file -leaf

$basename = $leaf -replace '(\d+)?(\..+)$', '' # hoge001.foo -> hoge

gci "$parent" "$($basename)*$($matches[2])" -file | % { # hoge*.fooなファイル
	$_.FullName -match '(\d+)?\..+$' | Out-Null
	$parts = ( $ts, $title, $matches[0] ) | ?{ $_ -ne '' }
	$leaf = $parts -join ' '
	$fullname = Join-Path $parent $leaf
	ren $_.FullName $fullname
}

batファイル

powershell.exe "C:\script\rename.ps1" %1

使い方
CubdPDFの外部プログラムにbatファイルを指定して、

> ps | Out-Printer CubePDF

を実行すると、プロセス一覧がCubePDFによってPDF, JPEG等に保存される。
ファイル名にはタイムスタンプ(任意)、指定したタイトルが入る。

GMail認証に失敗するとき

Send-MailMessageでハマったのでメモ。
GMailに5.5.1 Authentication Required.と言われる時は、新しい端末またはアプリからのログインなのでGMail独自の2段階認証が必要な可能性がある。
しかし2段階認証に非対応なアプリ(Send-MailMessageするスクリプトもこれにあたる?)もある。そんなアプリのためにGMailはアプリ固有パスワードを用意している。
https://support.google.com/accounts/answer/185833?hl=ja
ここで生成された固有パスワードを通常のパスワードの代わりに用いれば無事認証される。

使用したコード

$cred = Get-Credential $from
Send-MailMessage -To $to -From $from -Subject $subject -Body $body `
	-SmtpServer $smtp -Credential $cred -UseSsl -Port 587 -Encoding UTF8