バイトでサイズ欲しいんだけど計算めんどいな?計算しなくていい!

500MBのダミーファイル作りたくってな
500MBって何バイト?

答え

PS> 500mb

えええ…

1.5gb なんて書いてもOK

PowerShellからuwsスクリプトを実行しよう

uwsファイルがuwsc.exeに関連付けしてあれば Start-Process でuwsファイルパスを叩くといい感じ

# スクリプトに引数をわたしつつ、終了を待つ
Start-Process .\hoge.uws -ArgumentList fuga,piyo -PassThru | Wait-Process

Start-Process-PassThru するとちゃんと uwsc.exe のプロセスが返る
なので Wait-Process で終了を待てるというすんぽーさ
-ArgumentList に渡した配列は PARAM_STR[] に入るよ

// hoge.uws

if length(PARAM_STR) then
    msg = join(PARAM_STR, "<#CR>")
else
    msg = "PARAM_STR なし"
endif

msgbox(msg)

上のコマンドで呼ぶのがこんなスクリプトだった場合
ダイアログ閉じるまでPowerShellは待機するぞ

それと、uwsスクリプトが exitexit で終了コードを返せばPowerShell側でちゃんと受け取れるよ

// fuga.uws

exitexit slctbox(SLCT_BTN or SLCT_STR, 0, "終了コードを選ぶ", 0, 1, 2, 300, 65535)
# 終了を待って、ExitCodeを確認する
$Uws = Start-Process .\fuga.uws -PassThru
$Uws | Wait-Process
$Uws | select exit*

なんかほら、便利そうな気がしてきたろう?

ValueFromPipelineByPropertyNameはAliasが一致したんでも良いらしい

なるほどなー

例えばこう

-Value というパラメータのAliasを FullName にしとくじゃろ

function Test-Pipe
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [Alias('FullName')]
        [string] $Value
    )
    process
    {
        return $Value
    }
}
PS> Get-ChildItem | Test-Pipe
D:\Test\foo.txt
D:\Test\bar.txt
D:\Test\baz.txt

Aliasを CreationTime に変更すれば

function Test-Pipe
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [Alias('CreationTime')]
        [string] $Value
    )
    process
    {
        return $Value
    }
}
PS> Get-ChildItem | Test-Pipe
06/08/2016 15:03:06
06/08/2016 15:08:12
06/08/2016 15:08:12

な?

PowerShellでSyslogを読もう

リアルタイムでSyslog眺めたいときって、ありますよね
それが普段使ってるPowerShellで見れたら素敵ですよね

モジュール書こうか!な!

$Script:ReceivedSyslog = @()

class Syslog
{
    [datetime] $ReceivedTime
    [System.Net.IPAddress] $IPAddress
    [int] $Port
    [string] $Log
}

enum Severity
{
    emerg
    alert
    crit
    err
    warning
    notice
    info
    debug
}

function Receive-Syslog
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$false)]
        [ValidateSet('utf-8','shift_jis','euc-jp')]
        [string] $Encoding = 'euc-jp',
        [Parameter(Mandatory=$false)]
        [uint16] $Port = 514,
        [Parameter(Mandatory=$false)]
        [int] $ReceiveSpan = 1000,
        [Parameter(Mandatory=$false)]
        [scriptblock] $Parser
    )
    try
    {
        $Enc = [System.Text.Encoding]::GetEncoding($Encoding)
        $udpClient = New-Object System.Net.Sockets.UdpClient $([int] $Port)
        $RemoteEndpoint = New-Object System.Net.IPEndPoint @([ipaddress]::Any,0)
        $udpClient.Client.ReceiveTimeout = $ReceiveSpan
        do 
        {
            try
            {
                $ReceivedBytes = $udpClient.Receive([ref] $RemoteEndpoint)
                $Syslog = [Syslog] @{
                    ReceivedTime = Get-Date
                    IPAddress    = $RemoteEndpoint.Address
                    Port         = $RemoteEndpoint.Port
                    Log          = $Enc.GetString($ReceivedBytes).Trim()
                }
                $Output = if ($Parser) {
                    &$Parser $Syslog
                } else {
                    $Syslog
                }
                $Script:ReceivedSyslog += $Output
                $Output | Write-Output
            }
            catch
            {
                Write-Verbose 'timed out'
            }

        }
        while ($true)
    }
    finally
    {
        $udpClient.Close()
        Write-Verbose 'closed socket'
    }
}

function Get-SyslogReceived
{
    return $Script:ReceivedSyslog
}

function Clear-SyslogReceived
{
    $Script:ReceivedSyslog = @()
}

function Get-SyslogPriority
{
    param
    (
        [Parameter(Mandatory)]
        [int] $Int
    )
    return [Severity] ($Int % 8)
}

Receive-Syslog 叩けば受信するぞ

SyslogなんてUDPClientで受け取るだけなのでなんてこたーねぇ…
んだけど非同期の壁を乗り越えるのがアレだったので関数を実行するとブロックされます
Ctrl+C で停止させたかったので適度に Receive() をタイムアウトさせてますよ
雑だね!

Ctrl+C したときにソケットを閉じたいんだけどどうしたらいいんだ…
と暫く悩んでたんですが try-finally で良かったんですね
とても楽ちん、ありがとうPowerShell

そのまま実行すると [Syslog] 型にして返します
-Parser[scriptblock] を渡すと出力を好きに変更出来ます
$args[0][Syslog] 型の値が入ってるのでいい感じにしてから返しましょう
とりあえず元の値に NotePropertyAdd-Member する感じにしたら素敵でした

ほかのパラメータは

  • -Encoding
    へいしゃの製品はsyslogににほんごが混ざってる可能性があるのでそれらに対応する為に必要なんだ
    しょうじきやめてほしい
  • -ReceiveSpan
    UDPClientが受信でタイムアウトするまでの時間
    あんまり長くすると Ctrl+C してから止まるまでにイラッとするかもしれない
  • -Port
    待ち受けポートな

な感じです

関数を Ctrl+C で抜けても $Script:ReceivedSyslog 受信したログが全部入ってるので Get-SyslogReceived で後から見返すことも出来るぞ
パーサー書くときこれ使って練習したので実装しといてよかった

あとはなんだ、よく使うパーサーを吐く関数とか書いといたらゴキゲンです
そんじゃこれでsyslog見ながら仕事するぞう

太洋社のコミック発売予定一覧のかわりを探そう

コミック発売予定一覧 | Books | 株式会社 太洋社

毎月ここの一覧スクレイピングしてあれこれしてたんですけど、4月分が取れない
どうしたことか、と思ってたらなぁ

かなしいお知らせがありました

お知らせ 2016.03.10

コミック発売予定一覧をご利用いただきありがとうございます。
1997年より発売予定一覧コンテンツを提供してまいりましたが、
2016年3月発売分をもって休止することになりました。
今後の発売予定一覧掲載につきましては、別サイトで行うよう調整中です。
詳細につきましては、追ってお知らせいたします。
※comiclist.jpのTwitter(@comiclist_jp)でもお知らせいたしますので、あわせてご確認ください。

いずれ代替サイトが出来るようなんだけど、すぐとはいかなさそう
代わりを探す必要がありますよ

で、欲を言うとAPIなんかあるとうれしい
ダメ元で探してみると

あった

新刊コミック一覧取得API – HiWの気になることメモ

こんなのが取れます

{

"column":["id","url","img","eisbn","date","ttl","ttl_kana","athr","dprc","bprc","ahid_id","jun_id","cpn_id","ser_id","lbl_id","s_rec","s_new","s_res","s_frc","f_adlt","fc1_id","fc2_id","fc3_id","viewer_id","viewer_url"],

"list":[
  [1,13854778,"non",9784776742227,0,"大正初恋洋裁店","non","梶山ミカ",-1,0,"ミッシィコミックスNextcomicsF",1,0,89,36,0,0,0,0,0,"4月1日",0,1,1,"http://search.books.rakuten.co.jp/bksearch/dt/g001001/bathr%B3%E1%BB%B3%A5%DF%A5%AB/?s=2"],
  [2,13854779,"non",9784776742234,0,"暴君ヴァーデルの花嫁 初夜編(6)","non","松本帆加",-1,0,"ミッシィコミックスNextcomicsF",1,0,89,36,0,0,0,0,0,"4月1日",0,1,1,"http://search.books.rakuten.co.jp/bksearch/dt/g001001/bathr%BE%BE%CB%DC%C8%C1%B2%C3/?s=2"],
  // 略
  [740,13855076,"non",9784778119768,0,"シガレットシュガー","non","嶋二",-1,0,"ショコラコミックス",3,0,89,36,0,0,0,0,0,"4月下旬",0,1,1,"http://search.books.rakuten.co.jp/bksearch/dt/g001001/bathr%C5%E8%C6%F3/?s=2"]
]
}

jsonとは名ばかりでだいたいcsvでした
でもスクレイピングするより全然手間がかかんないので大歓迎だ

jsonなので Invoke-RestMethod で楽ちんな気がしますが

  • UTF8だから日本語文字化けしちゃう
  • BOM付いててjsonをパースできない

のでだめでした
そのへんは Invoke-WebRequest して自力でどうにかせねばならん

どうにかします

$Uri = 'http://books.rakuten.co.jp/event/book/comic/calendar/js/booklist.json'
$response = Invoke-WebRequest -Uri $Uri
$rawBytes = $response.RawContentStream.ToArray()
$json = [System.Text.Encoding]::UTF8.GetString(
        $rawBytes[3..$($rawBytes.Length-1)]
    ) | ConvertFrom-Json

受け取ったデータを [byte[]] にしてBOM取り除くためにsliceして、UTF8にしてから ConvertFrom-Json します
簡単ですね
簡単でした

あとはまぁ

$json.list | %{$_ -join "`t"} | ConvertFrom-Csv -Delimiter "`t" -Header $json.column

みたいに使いやすく加工したらいいんじゃないかと思います

join して ConvertFrom-Csv なんてしてるけどもっとうまい方法はある気がする
あとで調べる

PowerShellでifの戻り値パイプ出来ないの巻

ifは値を返します
変数に代入できるよ

PS> $v = if ($true) {
        '良いです'
    } else {
        '良くないです'
    }
PS> $v | Write-Host
良いです

一行で書いてなんちゃって三項演算子にもなりますよ

PS> $n = 2
PS> "$($n)は$(if($n % 2){'奇数'}else{'偶数'})です"
2は偶数です

調子に乗ってパイプで渡そうとしました

PS> if ($true) {
        '良いです'
    } else {
        '良くないです'
    } | Write-Host
発生場所 行:5 文字:3
+ } | Write-Host
+   ~
空のパイプ要素は許可されていません。
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : EmptyPipeElement

だめでした
パイプで渡そうとすると空っぽなようです
残念だ…

追記

スクリプトブロックを&で実行すれば、パイプで渡せますよー。

しゅんさんからアドバイスを頂いたぞ!
さっそくやってみよう

PS> & {if ($true) {
        '良いです'
    } else {
        '良くないです'
    }} | Write-Host
良いです

やったね!
そうかそうか即時関数にすれば良かったのか…
いやしかし、値返すのとパイプで渡せるのはイコールではないのね
どういうことなんじゃろね?よくわからんね?
たまにこういうことしたくなるので気をつけよう…