自分用vimコマンド備忘録

自分用のvimコマンド備忘録
これだけ覚えておけば…

移動
gg ファイルの先頭
G ファイルの最後
:[行番号] 指定した行
0 行頭
^ 先頭文字
$ 行末
return 次の行の行頭
Ctrl-d 半画面進む
Ctrl-u 半画面戻る
Ctrl-f 1画面進む
Ctrl-b 1画面戻る
w 次の単語
b 前の単語
% 対応する括弧
インサートモードに移行
a カーソルの右にappend
o カーソルの下に1行空けてそこから入力開始
A 行末から入力開始
I 行頭から入力開始
C カーソル位置から行末まで削除
i カーソルの左にinsert
変更履歴
u 操作の取り消し
Ctrl-r 操作のやり直し
. 直前の操作を繰り返す
コピー、貼り付け
x カーソル上の文字を削除
yy 1行コピー
dd 1行切り取り
p 貼り付け
:r [ファイル名] ファイルの中身を挿入
検索
/[文字] 文字を順方向に検索
n 前回と同じ文字を順方向に検索
N 前回と同じ文字を逆方向に検索
* 現在のカーソル位置にある単語を順方向に検索
置換
:(%)s/[置換前]/[置換後]/(g)(c) 置換を行う。%,g,cをつけると動作が変わる。
% すべての行を置換 g 行全体を置換 c 置換するか確認する
~ 大文字/小文字を変更
Ctrl-a 現在のカーソル位置にある数字をインクリメント
Ctrl-x 現在のカーソル位置にある数字をデクリメント
ウインドウ
:sp (ファイル名) 水平分割
:vsp (ファイル名) 垂直分割
Ctrl-w w 次のウインドウに移動
Ctrl-w q 選択中のウインドウを閉じる
※すべてのウインドウを閉じるとvim終了
Ctrl-w o 選択中のウインドウ以外閉じる
:set scrollbind 選択中のウインドウを同期スクロールさせる
:vert diffsp [ファイル名] 他のファイルと差分を取る
タブ
:tabnew (ファイル名) 新しいタブで開く
gt 右のタブに移動
gT 左のタブに移動
Ctrl-w q 選択中のタブを閉じる
※ウインドウと同様
ビジュアルモード
v 文字単位
V 行選択
Ctrl-v 矩形選択
※矩形選択中に$で各行の行末まで選択
※矩形選択後にIやAで複数行の行頭、行末に文字を追加できる。
※矩形選択後にd(削除),c(削除して挿入モード),y(ヤンク)が効く。
バッファ
:ls バッファ一覧を表示
:b [番号] 番号のバッファを表示
:bw 現在表示中のバッファを完全に消す
マクロ
q[a-z] aからzの1文字の名前でマクロ記録開始
q[A-Z] aからzの名前の以前のマクロに追記開始
q マクロ記録終了
@[a-z] マクロ再生
その他
:! [外部コマンド] 外部コマンドを実行
※領域選択していると
外部コマンドの入出力に取り込まれて置換されます
!! [外部コマンド] 外部コマンドを実行し、結果を挿入
:w [ファイル名] ファイル名で保存する
何かしらの開くコマンド ファイルではなくディレクトリを開こうとすると
ファイルエクスプローラが起動する

JavaMailでどこかのSMTPサーバにメール送信する

Javaでメール送信する場合にはJavaMailっていうAPIを使うことは知っていたが、
実際に使ったことは無かったから試しにこれ使ってメールを送信してみた。
まず、JavaMailはjavax.mailパッケージ配下に必要なクラスがあるが、JavaEEに含まれるものなのでSE環境で使うには実装をクラスパスに追加する必要がある。
こっからダウンロードできるよ♪
JavaMail API
右のDownloadsってリンクを辿りダウンロードページヘGo。
ソースは以下のようになった。
Gistにもうpした。 JavaMail Example · GitHub
...と言うかそのまま貼り付ける技を身につけたw

送信するSMTPサーバは自前でなくても、プロバイダの奴とかを利用すれば良い。
デフォルトのポート以外で接続しなきゃいけないならポート番号の指定が必要。
もちろん、認証も必要。
という訳で、どうやって認証すればよいかちょっと悩んだがslf4jのソースにヒントがあったw
とりあえず、パスワード認証用のクラスを作らないといけないみたい。
ちなみに上の例を送信しようとすると、以下のようなメールが作られる。

From: majiky@example.com
Sender: majiky@example.com
To: to@destination.com
Message-ID: <806909308.0.1317482819763.JavaMail.majiky@smtp.example.com>
Subject: =?utf-8?B?44Oh44O844Or44KS6YCB44Gj44Gf5Lu244Gr44Gk44GE44Gm?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: base64

44Oh44O844Or44KS6YCB44KK44G+44GX44Gf77yB

これで、スパムメール送り放題だね!イェイ♪

javaで超簡易HTTPサーバを作ってみた。

JDK6からcom.sun.net.httpserverパッケージに含まれるHttpServerクラスを使って
超簡易HTTPサーバを作ってみた。

ソースをgistに上げてみた。
HttpBackdoor · GitHub


使用例(windows)

javac HttpBackdoor.java
java -cp C:\;.\ HttpBackdoor 8080

使い方は、引数にポート番号を指定して起動するとそのポートでサーバが起動して、
引数無しの場合は80番でサーバが起動。
どこをドキュメントルートにするかはクラスパス指定で行う。
javaのオプションで"-cp <なんかのパス>;.\"と指定すればOK。カレントディレクトリを"最後"につける。
デフォルトだとカレントディレクトリにクラスパスが通ってるはずなので、
ドキュメントルートにしたいディレクトリでjavaコマンドを実行すればいいはず。
なので、ただ単に"java HttpBackdoor"で起動して、ブラウザでhttp://ホスト名/にアクセスすれば見れる。
javaが入ってるwindows環境でちょっと他のマシンからブラウザ経由で何かをダウンロードさせたい場合に使える・・・気がしなくもないツールw
名前にBackdoorとつけたようにセキュリティに関して何も考えてないですw
親切にも指定ディレクトリのファイル一覧が見れるようになってますよ!

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URL;
import java.util.List;
import java.util.Map.Entry;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

/**
 * usage:<br>
 *  java HttpBackdoor<br>
 *  java HttpBackdoor 8080<br>
 *  java HttpBackdoor -cp &lt;docRoot&gt;.\ 8080
 *
 * @author maji_KY
 *
 */
public class HttpBackdoor implements HttpHandler {

	private static final Class<HttpBackdoor> cls = HttpBackdoor.class;
	private static final byte[] NOT_FOUND = "<html><head><title>404 - Not Found</title></head><body>404 - Not Found</body></html>".getBytes();
	private static final byte[] SERVER_ERROR = "<html><head><title>500 - Error</title></head><body>500 - Error</body></html>".getBytes();


	/**
	 * @param args port
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {

		HttpBackdoor httpBackdoor = new HttpBackdoor();

		int port = (args.length >= 1)?Integer.parseInt(args[0]):80;

		HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);

		server.createContext("/", httpBackdoor);
		server.start();

	}

	@Override
	public void handle(HttpExchange ex)  throws IOException {

		OutputStream out = ex.getResponseBody();

		try {

			printLog(ex);
			URL url = cls.getResource(ex.getRequestURI().toString());

			if(url != null) {

				File file = new File(url.toURI());
				System.err.println(file);

				byte[] buf;
				if(file.isFile()) {
					InputStream is = cls.getResourceAsStream(ex.getRequestURI().toString());
					buf = new byte[(int) file.length()];
					is.read(buf);
					is.close();
				}else {
					StringBuilder sb = new StringBuilder("<html><title></title><body><a href=\"../\">../</a><br>\n");
					for(File f : file.listFiles()) {
						String name = f.isDirectory()?f.getName()+"/" : f.getName();
						sb.append("<a href=\"").append(name).append("\">").append(name).append("</a><br>\n");
					}
					sb.append("</body></html>");
					buf = sb.toString().getBytes();
				}

				ex.sendResponseHeaders(200, buf.length);
				out.write(buf);
				System.err.println("ok\n");
			}else {
				try {
					System.err.println("not found\n");
					ex.sendResponseHeaders(404, NOT_FOUND.length);
					out.write(NOT_FOUND);
				} catch (IOException e) {
					e.printStackTrace();
				}

			}

		} catch (Exception e) {
			System.err.println("error\n");
			ex.sendResponseHeaders(500, SERVER_ERROR.length);
			out.write(SERVER_ERROR);

			e.printStackTrace();
		} finally {
			out.close();
			ex.close();
		}

	}

	private void printLog(HttpExchange ex) {
		Headers headers = ex.getRequestHeaders();

		System.err.println(ex.getRemoteAddress());
		System.err.print(ex.getRequestMethod());
		System.err.print(" ");
		System.err.println(ex.getRequestURI());
		for(Entry<String, List<String>>  h : headers.entrySet()) {
			System.err.print(h.getKey()+":");
			System.err.println(h.getValue());
		}
		System.err.println();
	}

}

ソース見てお分かりの通り、でかいファイルにアクセスすると…

javascriptライクにdomを操作できるJavaライブラリ「jsoup」

Java使ってツールみたいなものを作るとき、
httpでアクセスすること自体は簡単にできるけど肝心のhtmlがうまく扱えないってことがあった。
で、いいものがないか調べてみると「jsoup」というライブラリがあるのを知ったので使ってみようと思う。

jsoup Java HTML Parser, with best of DOM, CSS, and jquery


使い方がとっても簡単。
というのは、htmlのコーディングで見慣れたメソッドばかり。

例えば、

getElementById(String id)
getElementsByTag(String tag) //Nameが無いけど。
getElementsByClass(String className) //Nameが無いけど。

とか、

attr(String key) 
text() 
html() 

などはjQueryそのまま。

あと、特徴的なものとしてjQueryのselectorみたいなものも実装してる!
"#idName"とか".className"とか"td:gt(1)"とか

	long start = System.nanoTime();
	Document document = Jsoup.connect("http://www.nicovideo.jp/ranking/fav/daily/imas").get();

	Elements spans = document.select("span[style=color:#C00;]");
	System.out.println(spans.size());
	for(Element span  : spans) {

		Element div = span.parent().parent();

		System.out.println(div.html());
		System.out.println("-----------------------------------------------------------------");

	}

	long time = System.nanoTime() - start;
	System.err.println("execute: "+time/1000000d+"msec.");

上は、ニコニコ動画アイマスデイリーランキングの中で最近の投稿(日付が赤く表示)のものを表示してますw


あとは、アンカータグをリストアップしてみたり

	long start = System.nanoTime();
	Document document = Jsoup.connect("http://www.nicovideo.jp/ranking/fav/daily/imas").get();

	Elements elements = document.getElementsByTag("a");
	AnchorList list = new AnchorList(null);

	for( Element element  : elements) {

		list.add(new Anchor(element.attr("href"), element.html()));
	}

	System.out.println(list.toString().replace(',','\n'));

	long time = System.nanoTime() - start;
	System.err.println("execute: "+time/1000000d+"msec.");

AnchorListとAnchorは自作のクラスです・・・。


ちなみに、アンカータグをリストアップするプログラムの実行速度ですが
自分でInputStreamを一文字ずつ読んでアンカータグを抜き出したのと比較すると、

自作
execute: 329.89493msec.
memory: 24257.832KBytes

jsoup
execute: 751.655096msec.
memory: 26993.248KBytes

※memoryは実行終了時の使用中ヒープサイズ(あてにならないかも)

まぁ、ちょっとは違いますが手間を考えるとjsoup使いたくなる。。。
かかってる時間のほとんどがDOMを構築する処理みたいなので、
その後にたくさん処理することがあるのならやっぱりjsoup使う方が良いw
上の例でselectにかかってる時間は70msec.程度。


DOMを操作するメソッドも用意してあるのでファイルから読み込んでそれを弄るなんてことも出来るみたい。
あと、1からhtmlを作ることも可能っぽい。

	Document document = Document.createShell("test");
	document.title("TestTitle");
	document.body().appendElement("h1").text("Hello World");

	System.out.println(document);

結果:

<html>
 <head>
  <title>TestTitle</title>
 </head>
 <body>
  <h1>Hello World</h1>
 </body>
</html>

結構新しめのライブラリで、html5も対応してるみたいなので新しいタグもちゃんと認識するみたい。


どうでもいいけど、jsoupのAPIリファレンス綺麗だな。
Java本家もOracleになってJDK7でナウっぽくしたみたいだけどちょっと見辛くない?w

Macでターミナルを活用し始めたときに行った設定

エンジニアならMacでも「コマンドラインで操作した方が楽」って思う時がある。(はず)
でも、Macって「すべてGUIでやれ」って感じの初期設定なんだよなぁ・・・。
標準だとlsコマンドに色もつかないし。


そこで、.bashrcを作る。
が、Macってそのままだと.bashrc読み込まないらしい。
なので.bash_profileに以下の記述を追記して読み込んでいただく。

if [ -f ~/.bashrc ] ; then
. ~/.bashrc
fi


で、以下のサイトを参考に新たに.bashrcで設定をしてみた。
http://blog.miraclelinux.com/asianpen/2007/04/bashrc_0c5f.html

#
# .bashrc 
#

# プロンプトにマシン名(\h)とカレントのフルパス(\w)を表示
#
PS1="\[[\u@\h \W]\\$ \]"


#
# grep -----------------------------------------------------------
#
# GREP_COLORは、検索ワードを色づけするために使用。
# 色づけすると、視認性が格段にあがる。
# -Eオプションは、拡張正規表現を使用する場合に指定
#
export GREP_COLOR='1;37;41'
alias grep='grep -E --color=auto'


#
# Aliases --------------------------------------------------------
#

#
# -iは確認を行う。-vは詳細な情報の表示。
#
#alias cp='cp -iv'
#alias rm='rm -iv'
#alias mv='mv -iv'

#
# odは16進数でのバイナリダンプコマンド
# -tx1zは、1バイトごとに区切って表示
# -Axはアドレスを16進数表示、-vは連続する0をスキップしない
#
alias od='od -tx1z -Ax -v'


#
# ls -------------------------------------------------------------
#
alias ll='ls -l'


#
# lsのカラー化
#
export LS_COLORS='no=01;37:fi=00:di=01;36:ln=01;32:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=40;32;01:ex=01;33:*core=01;31:'
alias ls='ls -G'


#
# オリジナルのTERM=xtermはカラー表示できないと思われる。
#
if [ "$TERM" == xterm ]; then
  export TERM=xterm-color
fi

さて、vimも使いやすくするために.vimrcを書き換えてみる。
下記のサイトを参考にした。
Vim初心者的導入メモ 2/3 「vimrc設定」編 - ナレッジエース

"シンタックスハイライト有効
:syntax on

"新しい行のインデントを現在行と同じにする
set autoindent
"バックアップファイルを作るディレクトリ
set backupdir=$HOME/vimbackup
"ファイル保存ダイアログの初期ディレクトリをバッファファイル位置に設定
set browsedir=buffer 
"クリップボードをWindowsと連携
"set clipboard=unnamed
"Vi互換をオフ
set nocompatible
"スワップファイル用のディレクトリ
set directory=$HOME/vimbackup
"タブの代わりに空白文字を挿入する
"set expandtab
"変更中のファイルでも、保存しないで他のファイルを表示
"set hidden
"インクリメンタルサーチを行う
set incsearch
"タブ文字、行末など不可視文字を表示する
"set list
"listで表示される文字のフォーマットを指定する
set listchars=eol:$,tab:>\ ,extends:<
"行番号を表示する
"set number
"シフト移動幅
"set shiftwidth=4
"閉じ括弧が入力されたとき、対応する括弧を表示する
set showmatch
"検索時に大文字を含んでいたら大/小を区別
set smartcase
"新しい行を作ったときに高度な自動インデントを行う
set smartindent
"行頭の余白内で Tab を打ち込むと、'shiftwidth' の数だけインデントする。
set smarttab
"ファイル内の <Tab> が対応する空白の数
"set tabstop=4
"カーソルを行頭、行末で止まらないようにする
"set whichwrap=b,s,h,l,<,>,[,]
"検索をファイルの先頭へループしない
"set nowrapscan


"入力モード時、ステータスラインのカラーを変更
augroup InsertHook
autocmd!
autocmd InsertEnter * highlight StatusLine guifg=#ccdc90 guibg=#2E4340
autocmd InsertLeave * highlight StatusLine guifg=#2E4340 guibg=#ccdc90
augroup END


"全角スペースを視覚化
highlight ZenkakuSpace cterm=underline ctermfg=lightblue guibg=#666666
au BufNewFile,BufRead * match ZenkakuSpace / /


さて、ちょっとわからないことがあってmanコマンドで調べてみると…英語だ。
英語ペラペーラではないゆとりにとっては、
まろやかにストレスを味わうことになるので以下のサイトを参考にどうにかしてみた。

http://satomikko94.sakura.ne.jp/MT/satomikko94b/2011/05/macmacman.html


.bashrcにも以下を追記。

#
# man ------------------------------------------------------------
# jmanで日本語化
#
alias man='env LANG=ja_JP.UTF-8 /usr/local/bin/jman'


これで少しは使いやすくなったかな。

「便利!簡単!すぐ出来る!」雑用に役立つJScriptを見直してみた。

巷ではnode.jsが流行っている、そんなご時世だから(?)JScriptを使ってみた。
いや、サーバーサイドJSとはジャンルが違うけど。
とにかくJavaScriptで簡単にバッチが書けるという噂の言語を書いてみたんだ。

で、JScriptって?

  • JScriptの特長
    • JavaScript1.5互換
    • Windows上で動き、ファイルアクセスも出来る
    • コンパイル無しで.jsファイルをダブルクリックですぐ実行
    • WindowsXPをインストールした段階で実行可能


JScriptは上記のような利点を持っていて
外部のネットワークと隔離されて自由にソフトは入れられない環境でも
誰もが少しは知ってるJavaScriptで書いてすぐ動かせるし、
ITドカタにはうってつけの言語なのだ!(発言の責任は持たない)


で、何をするのかというと日々の作業の中で自動化出来るものを
JScriptで書いてバッチ化することにより作業時間を短縮し、
仕事をしているふりをする時間を増やそうという作戦である。


つまり、以下のようなことに役立つのではないかと。

  • ダミーの連番のテストデータ作成(1回しか必要ないけど整合性が必要なものとか)
  • 大量の出力結果の確認(深いとこにあるフォルダを順に見ていくとか)
  • 仕事がはかどっているふりをするとき(あともう少しで定時だ!)

JScriptでファイル操作

とりあえず、どんなバッチつくるにもファイル操作は必須だと思うのでその方法を。
ファイル操作にはFileSystemObjectを使う。
具体的には以下のコードを書いてみた。テキストファイルの内容を読み取るだけ。

//ファイル操作のためのオブジェクトFileSystemObjectをnew ActiveXObject()で取得。
var fs = new ActiveXObject("Scripting.FileSystemObject");
//定数を自分で宣言・・・しなきゃならない。(普通に数値突っ込んでもいいけど分かりづらすぎるw)
var MODE = {
	//ファイルオープンモードの列挙
	READING: 1,
	WRITING: 2,
	APPENDING: 8
};
//ファイルオブジェクト取得
var file = fs.getFile("test.txt");
//JavaのFileInputStream的な感じ(バッファリングが既にされてるし、InOutの区別はないけど。)
var fis = file.OpenAsTextStream(MODE.READING);
/*
↓これでもファイルのストリームを取得できる
var fis = fs.openTextFile("test.txt", MODE.READING);
*/
var buf = "";
//ファイルの終わりまで1行づつ読み込む
while (!fis.atEndOfStream){
	buf += fis.readLine() + "\r\n";
}
fis.close();//ストリームは閉じる

//WScript.echoでメッセージ出力
WScript.echo(buf);

//WScript.quitで引数で指定した終了コードでプロセス終了
//別になくても一番下までいけば落ちるけど
WScript.quit(0);

"test.txt"の内容

本日は
晴天ナリ。

出来上がったコードを拡張子「.js」ファイルで保存してそれをダブルクリックするだけで動く!


実行結果。よし。


ファイル書き込み例。
ログファイル的なノリのゴミファイルを出力してみる。

var fs = new ActiveXObject("Scripting.FileSystemObject");
var MODE = {
	READING: 1,
	WRITING: 2,
	APPENDING: 8
};

function fillzero(num, length){
	var numStr = (""+num);
	var numStrLength = numStr.length;
	while(numStrLength++ < length){
		numStr = "0" + numStr;
	}
	return numStr;
}

var today = new Date();
var filename = today.getFullYear() + 
		fillzero(today.getMonth()+1, 2) + 
		fillzero(today.getDate(), 2) + ".txt";

//第3引数を追加してtrueを指定で開くファイルが存在しない場合勝手につくってくれる。
var fos = fs.openTextFile(filename, MODE.APPENDING, true);

var buf = "";
//1行書きこむ(追記モード)
fos.writeLine(today.toString() + " 実行!");
fos.close();

WScript.quit(0);

実行するごとにファイルに追記していくプログラムになっている。



よく使いそうなファイル操作

var fs = new ActiveXObject("Scripting.FileSystemObject");
var fis = fs.openTextFile("test.txt", 1);
var fos = fs.openTextFile("test2.txt", 2, true);

//TextStream#readAll:開いたファイルの内容をすべて取得出来る。Javaにはない。ちょっといいかも。
text = fis.readAll();
//TextStream#writeLine:1行書き込み。ファイルを上書きまたは追記モードで開く必要あり。
fos.writeLine("こんにちわ World!!");
//FileSystemObject#copyFile:ファイルコピー。
fs.copyFile("src.txt", "dist.txt");
//FileSystemObject#fileExists:指定したファイルが存在すればtrue。
WScript.echo( fs.fileExists("test.txt")?"ある":"ねーよ" );

その他はMSDNを参照

FileSystemObject リファレンス
http://msdn.microsoft.com/ja-jp/library/cc409800.aspx

JScriptWindows Script Hostの機能を使用

何かを処理するためには、引数とか取得できたほうがなにかと便利。
引数は「WScript.arguments」に入っている。


argumentsの型はWshArgumentsというオブジェクトで、
これはコレクションというJScript独自の、集合を表すオブジェクトの仲間で、
複数の引数を入力した場合もそれぞれ取り出すことができる。
が、このコレクションを処理する場合、JScript独特の書き方になる部分が登場。
JScriptのEnumeratorクラスを使わないといけない場面が出てくる。

var args = WScript.arguments;//とりあえず、長いので変数に入れ替えるw

/*
JavaScriptの配列やオブジェクトではない
WScript.argumentsはfor inでは処理できない。
for(var arg in args){//動かない
	WScript.echo(args[i]);
}
*/

//Enumeratorクラスを使って処理する。引数にコレクションを渡してインスタンス化。
var iterator = new Enumerator(WScript.arguments);
//JavaのIteratorに似たことをする。
for(;!iterator.atEnd();iterator.moveNext()){
	item = iterator.item();
	WScript.echo(item);//itemには引数のStringが入っている。
}


//ちなみにlengthが取得できるので普通のfor文でも書ける。
var len = args.length;
for(var i = 0; i < len; i++){
	//コレクションの要素にインデックスでアクセスするには
	//args[i]ではなくargs(i)と書く。メソッドだから。
	WScript.echo(args(i));
}

WScript.quit(0);

Enumeratorを使うとなんか目がチカチカするコードが出来上がりそうな…
なのでlengthを取得して書いたほうがシンプル。あれ?Enumeratorいらない子

  • 追記:

FileSystemObjectのFilesとかFoldersのコレクションは
Enumerator使わないと反復処理出来なかった・・・。少しだけいる子だった…。


ファイルを引数にしたい場合、
エクスプローラ上で引数にしたいファイルを.jsファイルの上にドラッグアンドドロップすると
そのファイルの場所のフルパスを引数として起動する。
複数同時にドラッグアンドドロップした場合もコレクションを処理するからそれぞれ取得できるわけ。



ついでにシェルのコマンドを実行出来るWshShellオブジェクトも書いたので紹介してみる。
WshShell.runメソッドでコマンドプロンプトで使えるコマンドを実行することが可能!

var shell = new ActiveXObject("WScript.shell");
var WINDOW_STYLE = {
	HIDE: 0,
	SHOW: 1
	//この他にもたくさんあるので後述のリファレンス参照
};


//runメソッドには
//「コマンド, ウインドウスタイル, 実行したコマンドが終了するまで待つか」を指定。
shell.run("cmd /K java -version", WINDOW_STYLE.SHOW);//第3引数を省略の場合false
WScript.echo("コマンド実行後すぐにここに来る");


var ret = shell.run("calc", WINDOW_STYLE.SHOW, true);//終了まで待つ場合は終了コードが得られる
WScript.echo("コマンド実行完了後にここに来る\r\n終了コード:" + ret);

WScript.quit(0);


応用すると任意のフォルダをエクスプローラで開くことが出来る。

var fileProtocolHandler = "rundll32.exe url.dll,FileProtocolHandler ";
var path = "C:\\";
shell.run(fileProtocolHandler + path);//Cドライブを開いた状態のエクスプローラが立ち上がる
var file = "test.txt";
shell.run(fileProtocolHandler + file);//ファイルを関連付けられたプログラムで起動


runメソッドで物足りない場合は、
WshShell.execメソッドを使うと実行中のアプリケーションへのストリームを取得できる。

//WshScriptExecが取得出来るのでそこから実行ステータスやストリームを取得する。
var exeObj = shell.exec("cmd /C for %a in ('バッチ','ファイル','見辛いな') do echo %a");
//status :実行ステータス
//stdIn  :標準入力
//stdOut :標準出力
//stdErr :標準エラー


//コマンド実行終了まで待つ。
while(exeObj.status == 0){//statusが実行中の場合0
     WScript.sleep(100);
     //実行中はstdInも有効。
}

WScript.echo(exeObj.stdOut.readAll());//ストリームで使えるメソッドが使える。
WScript.echo(exeObj.stdErr.readAll());

標準出力を表示した結果…

echo offしてねぇ〜。シングルクォート付いてるし。
だらしない実行結果…。


Windows Script Hostのリファレンス
http://msdn.microsoft.com/ja-jp/library/cc364453.aspx

任意のフォルダをエクスプローラで開く方法に関しては以下のサイトを参考にさせて頂きました。
他にも実用的なコードが載っています。
http://www.happy2-island.com/vbs/

JScriptXMLファイルを処理

みんな大好きDOMを使ったXMLファイルの調理をJScriptで行おうと思います。

とりあえず、そこら辺に転がってたxmlを読ませることにします。
build.xml

<?xml version="1.0" encoding="UTF-8"?>
<project name="JScript" default="default" basedir=".">
    <description>Builds, tests, and runs the project JScript.</description>
    <import file="nbproject/build-impl.xml"/>
        <target name="run" depends="JScript-impl.jar">
            <exec dir="bin" executable="launcher.exe">
                <arg file="${dist.jar}"/>
            </exec>
        </target>
</project>
//XMLDOMパーサを取得
var dom = new ActiveXObject("MSXML2.DOMDocument");

//loadメソッドでxmlドキュメントを読み込み
dom.load("build.xml");

//あとは標準的なDOM APIを使ったプレイが楽しめる
var nodeName = dom.documentElement.firstChild.nodeName;
WScript.echo(nodeName);//description

//IXMLDOMNode#selectNodesメソッドでXPathを使用したノードリストの取得もできる。
var nodeList = dom.documentElement.selectNodes("//exec/@*");

//IXMLDOMNodeListはコレクションなので
//for inを使った処理はできない。
var iterator = new Enumerator(nodeList);
for(;!iterator.atEnd();iterator.moveNext()){
	item = iterator.item();
	WScript.echo(item.nodeName);
}//[dir],[executable]の2つのノードが取得できる。


//これでも取得可能
var len = nodeList.length;
for(var i = 0; i < len; i++){
	WScript.echo(nodeList[i].nodeName);
}

このような感じでXMLの処理を行える。

MSXMLリファレンス(英語)
http://msdn.microsoft.com/ja-jp/library/ms761386.aspx


ところでコレクションをfor inで処理できないのはやっぱ不満な感じがする。
下みたいなやつを書いてみた。

//新しくイテレータを作る方針でやってみた
var iterator = function(collection){
	return {
		forEach : function(callback){
			var len = collection.length;
			for(var i = 0; i < len; i++){
				callback(collection(i));
			}
		}
	};
};

//iterator(コレクション).forEach(コールバック);で書ける
iterator(nodeList).forEach(function(node){
	WScript.echo(node.nodeName);
});

とりあえずは、考えることが減ったので
新しくWindowがインストールされたPCが支給されたときはメモ帳を立ち上げ、
上記のコードを真っ先に書くことにするw

さて、なんか作る

なんかつくろうとしたけど、何も思い浮かばないので
ニコニコ動画のコメントデータXMLを処理しなきゃいけないことにしてみた。

処理概要:

コメントのユーザ毎にテキストファイルを作り、その中にその人のコメを書きだす。


・・・特に、意味は無い。(全くもって誰得)

ちなみに、ニコニコ動画のコメントXMLはこんな感じ。

以下、ソース

var workPath = "R:/output/";
var xmlPath = "sm5777427.xml";

var startTime = new Date();//開始時間

//標準でforEachがあればいいのに…
var iterator = function(collection){
	return {
		forEach : function(callback){
			var len = collection.length;
			for(var i = 0; i < len; i++){
				callback(collection(i));
			}
		}
	};
};

//コメントのdate値(投稿日時)を読める文字列に変換
var getDateString = (function(){
	var week = ["(日)\t","(月)\t","(火)\t","(水)\t","(木)\t","(金)\t","(土)\t"];
	return function(sec){
		var date = new Date(sec * 1000);
		return date.toLocaleDateString() +
				week[date.getDay()] +
				date.toLocaleTimeString();
	}
})();

//コメントのvpos値(動画のどこでコメしたか)を読める文字列に変換
var getVposString = function(vpos){
	var time = (vpos / 100) >> 0;
	var min = (time / 60) >> 0;
	var sec = ("0" + (time % 60));
	return min + ":" + sec.substr(sec.length - 2);
};

//使い捨てのスクリプトなのでグローバル変数を大量生産している
var fs = new ActiveXObject("Scripting.FileSystemObject");
var dom = new ActiveXObject("MSXML2.DOMDocument");

//フォルダがないなら作る
if(!fs.folderExists(workPath))fs.createFolder(workPath);

dom.load(xmlPath);

var doc = dom.documentElement;

//XPathを使ってuser_id属性ノードを根こそぎ処理する
var nodeList = doc.selectNodes("//@user_id");

//HashSet的なことがしたい…
var userSet = [];
iterator(nodeList).forEach(function(item){
	userSet[item.nodeValue] = null;
});

//ユーザーごとに処理を行う
for(var userId in userSet){

	var buf = "";
	//指定したユーザー名の属性値を持つchat(コメント)ノードのリストを得る!
	var chatList = doc.selectNodes("//chat[@user_id = '" + userId + "']");
	
	//コメントごとの処理
	iterator(chatList).forEach(function(node){
	
		var dateStr;
		var vposStr;
		//dateとvpos属性の値を処理
		iterator(node.attributes).forEach(function(attr){
			switch(attr.nodeName){
				case "date":
					dateStr = getDateString(attr.nodeValue);
					break;
				case "vpos":
					vposStr = getVposString(attr.nodeValue);
					break;
				default:
			} 
		});
		
		buf += dateStr + "\t" +
			   vposStr + "\t" + 
			   node.firstChild.nodeValue + "\r\n";
		
	});
	
	//「ユーザーID.log」で保存
	fos = fs.createTextFile(workPath + userId +".log", true);
	fos.write(buf);
	fos.close();
	
}
WScript.echo("出力完了。\r\n" + (new Date() - startTime) + "msec");

WScript.quit(0);

ユーザごとに書き込み日時、書き込み時間、コメントが出力されるはず。
実行したところ。

で、実行した結果がこれだよ!

無駄なファイルがいっぱい出来ちゃった。。。


これは酷い

HTML5 の Web Workersを試してみる

Webが好きな自分としてはHTML5は新たな可能性を感じずにはいられない!
今更だけどこれからいろいろ試してみようと思う。
HTML5の中でも今までJavascriptでは出来なかった
マルチスレッドでの処理ができるWeb Workersが気になって試してみた。


注)javascriptの仕様を中途半端に知ってる人なのでおかしいところがあるかも。


さっそく何か書いてみる。
環境はWindows版Safari5。実験結果には責任は持たないw

テスト1
<!DOCTYPE html>
  <head>
    <meta charset="utf-8" />
    <title>Web Workersを試してみる</title>
    <script>
      
      onload = function(){
        var showDiv = document.getElementById("show");
        
        document.getElementById("button").addEventListener("click", function(){
          var start = new Date();

          showDiv.innerHTML = "";
          
          // Web Workers
          var myWorker = new Worker("work.js");
          myWorker.addEventListener("message", function(event){
            showDiv.innerHTML += event.data + ":" +(new Date() - start)+"msec.<br />";
          }, true);
          
          myWorker.postMessage("1");
          

        }, true);
      
      }
    </script>
  </head>
  <body>
    <h1>Web Workersを使う</h1>
    <form action="./" method="get">
      <input type="button" id="button" value="doTest()" />
    </form>
    <div id="show"></div>
    
  </body>
</html>

new Worker("work.js");でワーカーオブジェクトを作成。
引数はワーカーオブジェクト用に作った別のjsファイル。
"message"のイベントリスナーをセットしたらpostMessageメソッドでワーカースレッドが動作する。


work.jsファイルの中身。

addEventListener("message", function(event){
	//重たい処理
	for(var i = 0; i < 1000000; i++){
		new Date();
	}
    postMessage(event.data);
}, false);

こっちも"message"イベントを受け取って
postMessageでUIスレッドに結果のメッセージを返すように作る。
event.dataにはpostMessageの引数が入っている。

結果

Web Workersを使う


1:244msec.

ボタンを押したあとワーカースレッドで処理が行われ、
その後処理結果が表示される。時間はボタンを押してからの処理時間。


今度はWeb Workersを使った場合と
普通にUIスレッドで同じ処理を実行した場合を比較してみる。

スト2
<!DOCTYPE html>
  <head>
    <meta charset="utf-8" />
    <title>Web Workersを試してみる</title>
    <script>
      
      onload = function(){
        var showDiv1 = document.getElementById("show1");
        var showDiv2 = document.getElementById("show2");
        
        document.getElementById("button1").addEventListener("click", function(){
          var start = new Date();
          var mListener = function(event){
            showDiv1.innerHTML += event.data + ":" +(new Date() - start)+"msec.<br />";
          };

          showDiv1.innerHTML = "";
          
          // Web Workers
          var myWorker1 = new Worker("work.js");
          myWorker1.addEventListener("message", mListener, true);
          
          var myWorker2 = new Worker("work.js");
          myWorker2.addEventListener("message", mListener, true);
          
          myWorker1.postMessage("WW1");
          myWorker2.postMessage("WW2");
          

        }, true);
        
        document.getElementById("button2").addEventListener("click", function(){
          var start = new Date();
          var mListener = function(event){
            showDiv2.innerHTML += event.data + ":" +(new Date() - start)+"msec.<br />";
          };

          showDiv2.innerHTML = "";
          
          //普通(?)の処理(UIスレッドで)
          work = function(arg){
            //重たい処理
            for(var i = 0; i < 1000000; i++){
              new Date();
            }
            mListener(arg);
          };
          work({data:"普通1"});
          work({data:"普通2"});
          
        }, true);
      }
    </script>
  </head>
  <body>
    <h1>Web Workersの並列処理</h1>
    <form action="./" method="get">
      <input type="button" id="button1" value="doTest1()" />
      <input type="button" id="button2" value="doTest2()" />
    </form>
    <div id="show1"></div>
    <div id="show2"></div>
    
  </body>
</html>

work.js

addEventListener("message", function(event){
	//重たい処理
	for(var i = 0; i < 1000000; i++){
		new Date();
	}
    postMessage(event.data);
}, false);
結果

Web Workersの並列処理


WW1:257msec.
WW2:261msec.
普通1:368msec.
普通2:732msec.

2種類の処理を別々に実行した結果の比較。
Web Workersを使った処理ではUIスレッドで処理するより断然速い?




スレッドの数をもっと増やしてみよう。

テスト3
<!DOCTYPE html>
  <head>
    <meta charset="utf-8" />
    <title>Web Workersを試してみる</title>
    <script>
      
      onload = function(){
        var showDiv = document.getElementById("show");
        
        document.getElementById("button").addEventListener("click", function(){
          var start = new Date();
          var mListener = function(event){
            showDiv.innerHTML += event.data + ":" +(new Date() - start)+"msec.<br />";
          };

          showDiv.innerHTML = "";
          
          // Web Workers
          var myWorker1 = new Worker("work.js");
          myWorker1.addEventListener("message", mListener, true);
          
          var myWorker2 = new Worker("work.js");
          myWorker2.addEventListener("message", mListener, true);
          
          var myWorker3 = new Worker("work.js");
          myWorker3.addEventListener("message", mListener, true);
          
          var myWorker4 = new Worker("work.js");
          myWorker4.addEventListener("message", mListener, true);
          
          var myWorker5 = new Worker("work.js");
          myWorker5.addEventListener("message", mListener, true);
          
          var myWorker6 = new Worker("work.js");
          myWorker6.addEventListener("message", mListener, true);
          

          myWorker1.postMessage("1");
          myWorker2.postMessage("2");
          myWorker3.postMessage("3");
          myWorker4.postMessage("4");
          myWorker5.postMessage("5");
          myWorker6.postMessage("6");
        }, true);
      }
    </script>
  </head>
  <body>
    <h1>Web Workers並行処理のテスト</h1>
    <form action="./" method="get">
      <input type="button" id="button" value="doTest()" />
    </form>
    <div id="show"></div>
    
  </body>
</html>
結果

Web Workers並行処理のテスト


1:638msec.
3:649msec.
2:694msec.
4:708msec.
6:751msec.
5:771msec.

結果2

Web Workers並行処理のテスト


3:675msec.
2:689msec.
1:690msec.
5:743msec.
4:746msec.
6:796msec.

100万回new Date()する処理。
マルチスレッドで動いてることが確認できる。


ワーカースレッドからグローバル変数を参照しようとしてみる。

テスト4
<!DOCTYPE html>
  <head>
    <meta charset="utf-8" />
    <title>Web Workersを試してみる</title>
    <script>
      
      var globalObj = "Test";
      
      onload = function(){
        var showDiv = document.getElementById("show");
        
        document.getElementById("button").addEventListener("click", function(){
          
          var dataObj = {data:"Some data"};
          
          var mListener = function(event){
            showDiv.innerHTML += dataObj.data + " ⇔ " + event.data.data +"<br />";
          };

          showDiv.innerHTML = "";
          

          var myWorker1 = new Worker("work.js");
          myWorker1.addEventListener("message", mListener, true);
          
          
          myWorker1.postMessage(dataObj);
          
        }, true);
      }
    </script>
  </head>
  <body>
    <h1>Workerスレッドからの参照</h1>
    <form action="./" method="get">
      <input type="button" id="button" value="doTest()" />
    </form>
    <div id="show"></div>
    
  </body>
</html>

work.jsをちょっと書き換える。

addEventListener("message", function(event){

	if(globalObj == null){}
	
}, false);

実行すると

ReferenceError: Can't find variable: globalObj

エラーが出る。
というか、ちょっと考えれば当然の結果だった。
それじゃあ、eventの引数について調べてみる。


引数eventはオブジェクトが渡ってきていてevent.dataに渡されたオブジェクトが入っている。
それを書き換えてみるけど・・・

addEventListener("message", function(event){
     
	var obj = event.data;
	obj.data = "data has been changed";
	
    postMessage(obj);
}, false);

Workerスレッドからの参照


Some data ⇔ data has been changed

いまさらだけど、この実行結果分かり辛いな。
「dataObj.data + " ⇔ " + event.data.data」右と左のオブジェクトで、
左の元のオブジェクトは全然書き変わってない。ワーカースレッドには値渡しでオブジェクトを渡すらしい。


別スレッドでガンガンアプリケーションを動かす(?)ってのは考えないほうがいいのかな。
でも、やってみたい。
で、ワーカースレッドのソースをいじる。

addEventListener("message", function(event){
    setTimeout(function(){
        for(var i = 0; i < 1000000; i++){
            new Date();//ここもワーカースレッドで動作。
        };
        postMessage(event.data);
    }
    , 2000);
}, false);

setTimeoutで処理されるfunctionもワーカースレッドで動作。
どうにかして何かに使えないかなぁ。
ちなみにfunctionを渡そうとしたけど無理でした。
何か(?)が渡るけどObject扱いになります。スレッド間で直接の参照は出来ない!




ちなみにテスト3(6スレッド並列処理)をFirefoxで動かした結果。
Firefox3.6(core2 duo 1.83GHz)

Web Workers並行処理のテスト
2:1344msec.
3:1347msec.
5:2230msec.
4:2290msec.
1:2459msec.
6:3215msec.

2スレッドづつ処理してる?


別な環境でもやってみた。
Firefox3.6(core i7 860)

Web Workers並行処理のテスト
2:3779msec.
3:3781msec.
1:3783msec.
5:7662msec.
4:7666msec.
6:7668msec.

3スレッド。
なんか、限界が低いみたいだね。
そしてcore i7の方が遅いのはどうしてか?BIOSの省電力設定のせい?




ChromeはローカルでWeb Workersが動かないらしい。
気づかずに小一時間悩んでしまった。


Chrome8(こいつだけサーバーに上げて実行)

Web Workers並行処理のテスト


1:349msec.
2:535msec.
3:878msec.
4:1089msec.
5:1274msec.
6:1464msec.

よーし動いた、うご……あれ?