のえら

技術備忘とかメモとか.間違いはつっこんでいただきたい所存.アフィリエイトはやっていません.

ウォンバットさんたちにハマった影響で多摩動物公園に行ってきた話

2014年9月頃のお話。


ウォンバットさんにハマった影響で、ウォンバットについて色々調べているうちに実物を見たくなり、ネットで調べてみたらいくつかの動物園にいるようだったので、割と近い『多摩動物公園』に行くことを決意。
渋谷乗り換えで行くのがよさそうだったため、ついでにアドアーズ渋谷店に寄ってカモノハシさんをGETした。


せっかくなのでと、笹野のリュックに格納して謎のカモノハシさんPRを行う。
「僕もウォンさんに会いに行くんだなぁ!」


多摩動物公園でぶらぶら。結構坂が多いので園内のバスを使うのがよさそう。


ウォンバットのチューバッカさんがいた!が、微動だにせず。。どうやら結構な年らしく、ほとんど動かないとのこと。残念だけども、それでも可愛いのはウォンバットだからか。。
f:id:noterr0001:20151031210105j:plain


園内にコアラもいたので見に行く。見た目がまんまぬいぐるみみたいでびっくりしたけど、それよりご飯のユーカリが置いてあったその横で寝てたw食べながら寝るコアラ可愛すぎか。
f:id:noterr0001:20151031210127j:plain


館内の看板にあったコアラの説明にウォンバットがいたw
f:id:noterr0001:20151031210338j:plain


休憩所ではコアラのソフトクリームがあった。可愛い。
f:id:noterr0001:20151031210254j:plain


休憩所にいたカンガルーのオブジェと記念撮影。ここでもカモノハシさんPRを行う。
f:id:noterr0001:20151031210230j:plain


鷹とか虎とか梟とか・・・可愛すぎか。
f:id:noterr0001:20151031210427j:plain
f:id:noterr0001:20151031210436j:plain
f:id:noterr0001:20151031210449j:plain


また春先に行こうかと思っていたら、11月に亡くなったとお知らせが。。衝撃過ぎる。。お悔やみ申し上げます。www.tokyo-zoo.net

ウォンバットさんたちグッズを妄想する:その3

f:id:noterr0001:20150817223421j:plain
なんとなく思いついたのでメモがてら。下書きすら消していないやる気のなさがうかがえるネタ。

たぶんそのうち自分で作りだすであろう、ウォンバットさんたちモチーフのエプロン。
コアラさんの胸のふさふさがポケットになっているのがポイント。


右下にこっそりメッシュスポンジのキウイさんがいるんだけど敢えて触れない。。

OracleでSpoolしたときに余計な改行が出力される

Oracleのレコードをcsvファイルに出力したとき、行と行の間になぜか余計な改行が出力されてしまうので原因と回避方法を調査した。

環境

サーバー:Windows Server 2005
DB:Oracle 8i
出力方法:SQL*Plus の spool

現象

spoolを使用してcsvファイルを出力したときに、行と行の間に余計な空白行が出力されていることがあった。
例)
こういう形式で出力されるはず

"hoge","foo","ba
r"
"hoge2","foo2","bar"

が、以下のような出力になっていた。

"hoge","foo","ba
r"
←ここが空白行
"hoge2","foo2","bar"

レコードをいじって発生するデータを確認したところ、出力対象のカラムいずれかに改行コード(LFまたはCRLF)が含まれていると上記のように空白行が出力されることが判明。
改行コードがCRの場合は発生しなかったため、LFを含んでいると発生する様子。
Stirling を使用して出力したcsvファイルを確認してみたところ、LFがCR+LFに変換されて出力されていることがわかった。これ自体はOSに合わせてSQL*Plusが変換してるんだろうと思うのだけど、なぜ余計な空白行出力に繋がるのかは不明。。
文字コードにズレが発生した??

解決方法

Oracle Spool adds extra LF - Stack Overflow

SQL*Plus のオプション RECSEP に OFF を指定して、レコード・セパレータを出力しないようにする。
デフォルトでは WRAPPED になっている。
http://otndnld.oracle.co.jp/document/products/oracle11g/111/doc_dvd/server.111/E05784-01/ch12040.htm#i2699269

#(疑問)このレコード・セパレータについてよく理解していなくて、出力しないようにしたのにレコード単位で行出力されるのはなんでなんだろ??

余談

改行自体をなくしてしまう場合は、対象のカラムに対して REPLACE を使用して改行コードを空白に置換する。

例)
改行コードCR, LF, CRLFすべてを空白に置換する場合
CR -> chr(13)
LF -> chr(10)
CRLF -> chr(13) || chr(10)

select
	replace(replace(replace(FOO, chr(13) || chr(10), ''), chr(10), ''), chr(13), '')
from
	hoge

PowerShellのあれやこれ

概要

PowerShellでファイル操作とかする中で、調べたりコマンドヘルプ見たり試したりして溜まった小ネタとかメモとか。パイプとか基本的な部分は書いてない。
ざっくばらんに調べた順になっているのでごちゃごちゃしてる。

小ネタ

入力

タブキー押下で入力補完してくれる。
カーソルがコマンドの間にあるときにタブキーを押下すると、カーソル以降の入力内容は消える。

UNIXコマンドの使用

大体のUNIXコマンドはPowerShellエイリアスがつけられているので使える。
たとえば「cat」「ls」「pwd」とか。

後述の「別名(エイリアス)の取得」も参照。

テキストファイルの行数計測

データベースのレコードから作成したcsvファイルに対して、各何行あるのか調べる。
ぱっと思いついたのはこんな感じ。

Get-Item * -Include *.csv | foreach -process { (Get-Content $_).length }

カレントディレクトリ内のcsvファイルを取得して、foreachでぐるぐるして行数取得してみたけど、一定のファイルサイズになると「'System.OutOfMemoryException' の例外がスローされました。」で落ちる。マジか。

PowerShell使わないでWindowsコマンドでやれば落ちなかったけど、これはこれでなんとなく悔しい。
※1行目はbatで保存したときに、実行したディレクトリに移動するコマンド

cd %~dp0
forfiles /m *.csv /c "cmd /c find /v /c """""" @path"

で、PowerShellで他に行数取得できないかコマンド調査。
計測関数「Measure-Object」コマンドというのがあった。パイプでファイルオブジェクト渡すとLineオプションで行数出力できる。
早速使ってみる。行数だけ欲しいのでFormat-Listでcountのみ出力。

Get-Content hoge.csv | Measure-Object | Format-List count

時間はかかるけどメモリエラーは発生しない。やったね。

他に、System.IO.FileStreamを使用してファイルを読み込み、1行読んだら変数増加、末行まで読んだら変数出力、というやり方がある。省略。

別名で保存

カレントディレクトリ内の特定のファイルに対して何か処理をして、元のファイル名に文字列を付加して保存するやり方。

例1)カレントディレクトリのcsvファイルに対して先頭から2行ずつ取得してそれぞれ別名で保存する(UNIXのhead相当)

Get-Item * -Include *.csv | foreach -Process { gc $_ -TotalCount 2 > $($_.BaseName + "_head_2.csv") }

例2)カレントディレクトリのcsvファイルに対して末尾から10行ずつ取得してそれぞれ別名で保存する(UNIXのtail相当)

Get-Item * -Include *.csv | foreach -Process { gc $_ | Select-Object $_.Path -last 10 > $($_.BaseName + "_last_10.csv") }

拡張子を含まないファイル名の取得はBaseNameプロパティを用いる。
文字列の結合は$()内で行う。これ知らなくて、最初括弧外して記述して、実行時エラーになるわ元のcsvファイル上書きしちゃうわでえらいことになった。。

foreachの話

foreachはForEach-Objectの別名だけど、%も別名なので、上記のコマンドはさらに短く書ける。

Get-Item * -Include *.csv | %{ gc $_ -TotalCount 2 > $($_.BaseName + "_head_2.csv") }

余談で、foreachの改行ってどうするのかと調べてみた。{}をPowerShellが認識するから特殊なコマンド入力とかは不要だよ!って見つけたけど、}を入力した後も「>>」が表示されるからあれ?と思ったら、空入力でエンターキー押せばいいだけだった。。

PS C:\> 30000,56798,12432 | foreach-object -process {
>> $_ / 1024
>> }
>>

↑ここで;とか入れるからだめだった、エンターキー押せば実行される。

.NET Frameworkメソッド呼び出し

たまに「 [System.io.XXX]::HogeHoge」のような記述をみかけるので試してみた。

[System.io.Path]::GetFileNameWithoutExtension("C:\Users\hoge\foobar.txt")

拡張子を除いたファイル名が返却される(この場合「foobar」)

ファイル一覧取得時の小ネタ

ディレクトリ内の一覧表示は ls とか Get-ChildItem とかでできるけど、この一覧をソートしたい場合はパイプで Sort-Object に渡してソート項目を指定する。
以下のコマンドは「最終更新日」でソートする。

Get-ChildItem | Sort-Object lastwritetime -Descending

何も指定しない場合は名前昇順でソートされる。
項目は最終更新日とかサイズとかを指定可能。
降順はDescendingオプションを指定する。

ディレクトリだけ抽出したい場合は PSIsContainer プロパティが使える。

Get-ChildItem -Recurse | ?{ $_.PSIsContainer } | Select-Object fullname

これでもいい。

Get-ChildItem -Recurse | Where-Object { $_.PSIsContainer }

再帰的に全ファイルを抽出するのはこんな感じで書ける。

Get-ChildItem -Recurse | ?{ -not $_.PSIsContainer } | Select-Object fullname
ファイルから文字列検索

ファイルから指定した文字列が含まれる行を検索するには「Select-String」が使える。

Select-String C:\hoge.txt "hoge"

なんだけど、日本語を指定する場合はオプション Encoding に Default を指定する必要がある。

Select-String C:\hoge.txt "ほげ" -Encoding Default
コマンド実行時間の出力

Measure-Command を使用する。

Measure-Command { Get-Content hoge.csv | Measure-Object | Format-List count }

出力結果:

Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 135
Ticks : 1359836
TotalDays : 1.57388425925926E-06
TotalHours : 3.77732222222222E-05
TotalMinutes : 0.00226639333333333
TotalSeconds : 0.1359836
TotalMilliseconds : 135.9836

フォーマットをHH:MM:SS.msにしたい場合はtoString()が便利。

(Measure-Command { Get-Content hoge.csv | Measure-Object | Format-List count } ).toString()

出力結果:

00:01:33.9708083

プロパティの確認

出力されるオブジェクトと、そのオブジェクトがどんなプロパティを持っているかは Get-Member で調べられる。

Get-ChildItem | Get-Member
別名(エイリアス)の取得

サンプルコードで見かけた「?{}」ってなんじゃろな、と思ったのでPowerShellに聞いてみる。

PS C:\> Get-Alias ?

CommandType     Name                                                Definition
-----------     ----                                                ----------
Alias           %                                                   ForEach-Object
Alias           ?                                                   Where-Object
Alias           h                                                   Get-History
Alias           r                                                   Invoke-History

ForEach-Objectの別名でした、と。
こんな感じで、構文見てなんとなく別名だろうな、と思った場合は「Get-Alias」で聞いてみるといい。「今は『ls』でファイル一覧を表示しているけど、PowerShellのコマンド覚えるかー」と思ったら Get-Alias 使ってコマンド確認する、という状況にも使える(しかしlsのほうが短くて便利である)
※別名の一覧を見たいときは引数なしで呼び出す

が、コマンドについて調べるなら次の「Get-Help」を使ったほうが早かったりする。

何はなくともGet-Help

ヘルプ情報を取得できる Get-Help コマンドが結構できる子。
コマンドのヘルプだけでなく、PowerShellでの別名を調べたり、キーワードから「もしかして」くらいに検索してくれたり、コマンドのサンプルを見たり、意外と有能。

たとえば、ファイル一覧抽出のサンプルを見ていたら「-le」というキーワードがあったけど、コマンドのオプションではなさそう、なんぞこれ?というような状況のときに、ヘルプに聞いてみる。

PS C:\> get-help "-le"

Name Category Synopsis

        • -------- --------

Split-Path Cmdlet 指定されたパス部分を返します。
about_arrays HelpFile データ要素を格納するためのコンパクトなデータ構造について説明します。
about_Break HelpFile Foreach、For、While、Do、または Switch ステートメントを直ちに終了するた...
about_Comparison_Operators HelpFile Windows PowerShell で値を比較する演算子について説明します。
about_For HelpFile 条件テストに基づいてステートメントを実行するのに使用する言語コマンドにつ...
about_operators HelpFile Windows PowerShell でサポートされている演算子について説明します。
about_remote_troubleshooting HelpFile Windows PowerShell でリモート操作のトラブルシューティングを行う方法について
about_scripts HelpFile Windows PowerShellスクリプトを作成および実行する方法について説明します。
about_Special_Characters HelpFile コマンドまたはパラメーター内でその次にある文字を Windows PowerShell がど...

leを含むであろうコマンドやヘルプの一覧が出力されるので、それらしいもの(※)をさらにヘルプで確認する。
(※)ファイル抽出の条件に使っているから演算子かな、というあたりをつける、というのは経験則に基づくのであまりいい例じゃなかった。。

PS C:\> get-help about_operators
トピック
about_Operators
(中略)
比較演算子
比較演算子 (-eq、-ne、-gt、-lt、-le、-ge) は、値を比較して条件をテスト
するときに使用します。たとえば、2 つの文字列値を比較して、それらが等
しいかどうかを判断できます。
(中略)
詳細については、「about_Comparison_Operators」を参照してください。

こんな感じで、ヘルプを追いかけていけばある程度解決できる。

ヘルプのバグ

ヘルプは日本語訳されてる。
が、format-listのヘルプ翻訳で、例2の説明では「これらの」とあるが、2番目のコマンドが記述されていない。
なお、ヘルプの英語版元ページ(https://technet.microsoft.com/ja-jp/library/hh849957.aspx)にはきちんと残っている。
ヘルプに違和感を感じたら英語版読んだほうがいいかも。めったにないと思うけど。

PS C:\> get-help format-list -detailed

名前
Format-List
(中略)
-------------------------- 例 2 --------------------------

C:\PS>$a = get-childitem $pshome\*.ps1xml

説明
-----------
これらのコマンドは、Windows PowerShell ディレクトリの PS1XML ファイルに関する情報を一覧形式で表示します。最初のコマ
ンドは、ファイルを表すオブジェクトを取得し、$a 変数に保存しています。2 番目のコマンドは、Format-List を使用して、$a
に保存されているオブジェクトに関する情報を書式設定しています。このコマンドは、InputObject パラメーターを使用して変
数を Format-List に渡します。変数を受け取った Format-List は書式設定された出力を表示するために既定の出力コマンドレ
ットに送ります。

※抜けているコマンド

PS C:\>format-list -InputObject $a

サイズの大きいcsvファイルから先頭・末尾を取得するときのメモ

csvファイルはサイズが大きく(2GBくらい)なると、ExcelでもテキストエディタでもACCESSでも開けなくなる。
(Windowsコマンドtypeで出力する分には問題ないんだけども)
そんなおっきなcsvファイルの先頭末尾を取得するときのメモ。
windowsコマンドではheadとtailがない。。

headとtailの代替コマンド
MS-DOS バッチファイル Tips - 翔星 Be ランド日記

バッチにして実行してみる

call head.bat *filename* 15 > *tempfilename*

→「このコマンドを実行するのに十分な記憶域がありません。」と出て実行できない。。


メモリが足りなくて実行できないのでPowerShellでやってみる
Windows equivalent of the 'tail' command - Stack Overflow
→この回答を参照してみた(http://stackoverflow.com/a/14341672)

head相当:

powershell -command "& {Get-Content *filename* -TotalCount *n*}"

→TotalCountは先頭から指定された行数取得するオプション
https://technet.microsoft.com/ja-jp/library/hh849787(v=wps.620).aspx

tail相当:

powershell -command "& {Get-Content *filename* | Select-Object -last *n*}"

→コレクション内の末尾から指定の行数を取得する(lastオプション)
 こっちはちょっと時間がかかる
 なお、Ver.3.0からGet-ContentにTailオプションが導入されたのでSelect-Objectを使わなくてもOK

※アンパサンドの意味は?
→引用符で囲まれたスクリプトを実行する場合は、その先頭にアンパサンドが必要

直接PowerShellから実行する場合は括弧内のコマンドだけでよい

おまけ:
PowerShell入門によい記事
https://technet.microsoft.com/ja-jp/scriptcenter/powershell_owner05.aspx