pgrep.cmd / pkill.cmd2008年09月29日 12時51分41秒

なんちゃってコマンド・番外編

ほんとはもうこの関連やらないつもりだっだんだけど、「FORコマンドで遊んでみる。」のコメント欄で、たけチンさんからリクエストをいただいたのでちょろっとやってみました。やっぱ同一名称のプロセス全殺しの需要があったか。

内容からして、基本は「FORコマンドで遊んでみる。」の「killでメモ帳を全て終了」で例示したコマンドをほぼそのまま使えるのでスクリプト作成なし、バッチファイルのみでやってます。

ただ、リクエストいただいたpgrep、pkillとも普段使ってないコマンドであまり詳しくないので、

最低限はプロセス名称でのgrepができればいいのですが、、、
という最低限の実装しかしてません。ご容赦くださいな。 > たけチンさん

pgrep.cmd

まずはプロセスIDを列挙するpgrepから。基本はforコマンドにps+findstrの出力を渡してechoするだけなんだけども、

バッチ プログラムで FOR コマンドを使用するときは、%変数の代わりに、%%変数を使用してください。
for /? より抜粋
ってな注意書きがあるので、そのあたりに気をつけてこんな感じ。
@echo off
for /f "tokens=1" %%p in ( '"ps -a| findstr -i %1"' ) do @echo %%p

この内容を「pgrep.cmd」とか「pgrep.bat」とかでパスが通ったところに保存すればOKです。まぁ、前提としてps.js、ps.cmd、kill.cmdがパスの通ったところに設置済みである必要がありますが。

いちおう以前配布したzipに含めたので、ps.jsとかを設置していない場合はこちらをダウンロードして、こちらのエントリを参考にして設置してみてくださいな。

技術的もは、findstr(というかクォートされたコマンド)にそのまま%1でバッチの引数渡せるか心配だったんだけど、やってみたらすんなり通ったのでこれで完成。

あ、findstrは無条件に -i オプション(ignore case)をつけちゃってます。だってwindowsだし。

pkill.cmd

もうね、さっきのバッチの「@echo」を「@kill」に変えるだけなんですよ。えぇ。

@echo off
for /f "tokens=1" %%p in ( '"ps -a| findstr -i %1"' ) do @kill %%p

設置の仕方はpgrep.cmdと同じです。当然zipに含めてあります。

試してみる

まず、メモ帳2つと、NOTEPAD++を起動した状態で、ps+findstrで確認。

C:\Documents and Settings\dara-j>ps -a | findstr -i notepad
 9564   164 dara-j               4940     4940    0:00:00 notepad.exe
 6456   164 dara-j               5048     5048    0:00:00 notepad.exe
 8360   888 dara-j                828    17328    0:00:00 notepad++.exe

C:\Documents and Settings\dara-j>
んでpgrepしてみる。
C:\Documents and Settings\dara-j>pgrep notepad
9564
6456
8360

C:\Documents and Settings\dara-j>
うん、3つ表示されますな。

じゃ、今度はメモ帳だけに限定。

C:\Documents and Settings\dara-j>pgrep "notepad.exe"
9564
6456

C:\Documents and Settings\dara-j>
うん、ちゃんとnotepad++(PID:8360)が除外されてますな。あ、パターンをクォートするときはダブルクォートにしてください。シングルクォートだとキチンとパターン(バッチ中の「%1」)に渡せないので。

最後にpkillの確認。メモ帳だけ全殺し。

C:\Documents and Settings\dara-j>pkill "notepad.exe"

C:\Documents and Settings\dara-j>pgrep "notepad.exe"

C:\Documents and Settings\dara-j>pgrep notepad
8360

C:\Documents and Settings\dara-j>
はい、notepad++だけは生き残ってます。当然画面上でもメモ帳のウィンドウがばっさり切られたと思います。ちなみに「pkill notepad」にするとnotepad++も殺されます。

関連記事

.svnディレクトリだけ列挙2008年09月07日 17時42分06秒

一週間ほど現実逃避をしていたら、もう9月すぎてるし。1ヶ月以上ブログ更新してないし。と思ったのだが、あんまりいいネタがないのでコネタ。

もうね、なんで気が付かないかな。

1ヶ月ほど前のエントリで「コマンドラインで.svnディレクトリを一括削除」なんてコマンドラインネタやったんだけど、このときは

  • dirコマンドのオプションに /b(ファイル名のみ表示)、/s(指定ディレクトリ以降の再帰処理)に加えて隠しディレクトリを対象にするために/aオプションで属性未指定(=すべての属性を対象)として、
  • 結果をfindstrの正規表現で「.svn」だけに絞り込む
という2段階の手続きを踏んでいた。こんな感じ。
dir /b/s/a | findstr /r \.svn$
うん、タイプするのめんどくさいわ。

でね、もう一回dirコマンドのヘルプ見てみると、

  /A          指定された属性のファイルを表示します。
  属性         D  ディレクトリ               R  読み取り専用
               H  隠しファイル                A  アーカイブ
               S  システム ファイル           -  その属性以外
(dir /? より抜粋)
だって。/aオプションでディレクトリのみに絞り込めるじゃん

そんなわけで

さっきのパイプ付き処理を置き換えると、こんな感じ。

dir /b/s/a:dh *.svn
/a:dh で「隠しディレクトリ」のみを対象とする、と。

だもんで、以前のエントリに掲載した.svnの再帰削除コマンド

for /f "tokens=*" %d in ('"dir /b/s/a | findstr /r \.svn$"') do rmdir /q/s "%d"

なんて面倒なコマンドは、(比較的)シンプルに
for /f "tokens=*" %d in ('dir /b/s/a:dh *.svn') do rmdir /q/s "%d"

なんてシンプルに置き換わります。無駄なファイル/ディレクトリの列挙もパイプ処理もないので当然こっちのほうがパフォーマンスいいし。測ってないけど。

あ、ファイル名の指定は通常のDOSワイルドカード指定になるので「.svn」じゃなくて「*.svn」になるとこだけは注意ね。

FORコマンドで遊んでみる。2008年08月04日 01時52分38秒

いや、実用性はないんだけども、前のエントリで覚えたばかりのFORコマンドと、ちょっと前に作ったなんちゃってコマンドを組み合わせて遊んでみたり。

chkconfig.jsの--detailを連続で

chkconfig.jsにオマケでつけてた--detailオプション、サービス名を指定しないと使えない仕様なんだけども、これをFORコマンドと組み合わせて使ってみると。

コマンドラインからcscript付きで指定するとちょっと冗長なので、ラッパーのバッチファイルを作っておくことにしよう。こんな感じで、「chkconfig.bat」とか「chkconfig.cmd」とか。

@echo off
cscript //nologo "%~dP0\scripts\chkconfig.js" %*

dara-jは特定のディレクトリにバッチ(というか.cmdファイル。単なる好みだけど)を設置することにしていて、そこにサブディレクトリ「scripts」を切って.jsはそこに置くようにしているので、chkconfig.jsのパス指定で「%~dP0\scripts\」としている。んで、バッチをおいたディレクトリにパスを通している。

で、chkncofig --listでサービスを列挙した後にfindstr Manualで手動起動設定になっているサービスのみに絞り込んで、それらに対してchkconfig --detailを適用してみる。

C:\Documents and Settings\dara-j>for /f "tokens=1" %d in ('"chkconfig --list | f
indstr Manual"') do @chkconfig --detail %d
サービス名 : AppMgmt
表示名     : Application Management
スタートアップの種類 : 手動
状態       : 停止中
説明 :
  割り当て、公開、削除などのソフトウェアのインストール サービスを供給します。

サービス名 : aspnet_state
表示名     : ASP.NET 状態サービス
スタートアップの種類 : 手動
状態       : 停止中
説明 :
  ASP.NET の out-of-process セッション状態のサポートを提供します。このサービスが
中止されると out-of-process 要求は処理されません。このサービスが無効になると、こ
のサービスに明示的に依存しているサービスはすべて開始できなくなります。
 :
(中略)
 :
サービス名 : xmlprov
表示名     : Network Provisioning Service
スタートアップの種類 : 手動
状態       : 停止中
説明 :
  自動ネットワーク提供のための XML 構成ファイルをドメインごとに管理します。


C:\Documents and Settings\dara-j>

結構重たい上なんの役にも立たないんだけども。

FORコマンド中で「tokens=1」と指定しているのは、chkconfig --listの出力結果からサービス名のみを抽出するため。FORコマンドの既定の区切り文字(空白とタブ、らしい)で各行がトークン単位に分割されるのだが、chkconfigの出力の場合は、先頭がサービス名になるのでそのように指定している。

killでメモ帳を全て終了

今度は多少は使い道あるかなぁ。こちらの「ps.jsとps.cmd / kill.cmd」で作ったpsとkillを使って、特定のプロセスをまとめて終了させると。

まず適当にnotepadをいくつか起動する。

C:\Documents and Settings\dara-j>ps | findstr notepad
 1456  5788 dara-j               4708     4708    0:00:00 notepad.exe
 5316  5788 dara-j               4852     4852    0:00:00 notepad.exe
 4228  5788 dara-j               4716     4716    0:00:00 notepad.exe
 4992  5788 dara-j               4720     4720    0:00:00 notepad.exe
 5544  5788 dara-j               4696     4696    0:00:00 notepad.exe

C:\Documents and Settings\dara-j>

こんな感じで5個のメモ帳が動いてるとして、
C:\Documents and Settings\dara-j>for /f "tokens=1" %d in ('"ps | findstr notepad
"') do @kill %d

C:\Documents and Settings\dara-j>

ってな感じでnotepad.exe全殺し。やっぱ役に立たないかな。

コマンドラインで.svnディレクトリを一括削除2008年08月04日 01時05分23秒

DIR コマンドにこんな使い方が

なにを探しているときに見つけたかすでに忘れたのだが、しばらく前にこんなTIPSを見つけた。

タイトルにあるとおり、

dir /b/s somedir
ってやると、UNIX/Linuxのfindコマンドよろしく、指定ディレクトリ以下のファイルをフルパスで列挙してくれる。

例えば、dara-jの手持ちの環境ではZend Framework 1.0.0を D:\PHP\libs\Zend 以下に設置しているのだが、こんな感じで列挙できる。

C:\Documents and Settings\dara-j>dir /b/s D:\PHP\libs\Zend\*.php
D:\PHP\libs\Zend\Acl.php
D:\PHP\libs\Zend\Auth.php
D:\PHP\libs\Zend\Cache.php
D:\PHP\libs\Zend\Config.php
D:\PHP\libs\Zend\Date.php
D:\PHP\libs\Zend\Db.php
D:\PHP\libs\Zend\Debug.php
 :
(中略)
 :
D:\PHP\libs\Zend\XmlRpc\Value\DateTime.php
D:\PHP\libs\Zend\XmlRpc\Value\Double.php
D:\PHP\libs\Zend\XmlRpc\Value\Exception.php
D:\PHP\libs\Zend\XmlRpc\Value\Integer.php
D:\PHP\libs\Zend\XmlRpc\Value\Scalar.php
D:\PHP\libs\Zend\XmlRpc\Value\String.php
D:\PHP\libs\Zend\XmlRpc\Value\Struct.php

C:\Documents and Settings\dara-j>
はぁ、こんなことできたんだぁ。

ヘルプで調べてみると

dir /?でそれぞれのオプションがどんな意味があるのか見てみたところ、

/B ファイル名のみを表示します (見出しや要約が付きません)。
dir /? より抜粋
/S 指定されたディレクトリおよびそのサブディレクトリのすべての ファイルを表示します。
dir /? より抜粋

なるほど、/bで属性などをつけずファイル名(というより、絶対パスみたいなのだが)のみ、/sで再帰処理なことはよくわかった。しかし、何度かこの説明文は見たことあるんだけど、さっぱり気がつかなかった。

その列挙を利用して...

これだけでも割と目から鱗チックだったんだけど、リンク先にはさらにこんなコマンドが載っていた。

リダイレクトしない場合はforコマンドが使えます。

C:\Inetpub\wwwroot> for /f %d in ('dir /b/s _vti_cnf') do rmdir /q/s %d

うーん、よくわからんが、どうやらIISがらみの特定のディレクトリかファイルを再帰処理で一括削除しているみたい。

つか、FORコマンド??

for /?で見てみると(長いヘルプなのでテキストファイルに書き込むと吉)、どうやらこれが該当するのかな。

FOR /F ["オプション"] %変数 IN ('コマンド') DO コマンド [コマンド パラメータ]
for /? より抜粋
説明を見てもなんだか冗長でイマイチ把握しづらいんだけど、()内の一重引用符で囲まれた「dir /b/s _vti_cnf」の結果を変数 %d に逐次当てはめながら、do 以降で指定されているコマンド「rmdir /q/s %d」に渡しているんだろうと。

なるほどねぇ、dir /b/s でファイルのフルパスがあがってくるので、これをforコマンドにまわせばループ処理できるんだ。

.svnディレクトリを再帰的に削除してみたり

んで、やっとこ本題。

Subversionの作業コピーって、各ディレクトリにメタデータディレクトリ「.svn」が隠しディレクトリとして追加されてるのよね。

ローカルで作業してるときはいいんだけど、これをどっかに配布したりするのに、このメタデータが邪魔なので削除してみようかと。いや、当然エクスポートすればいいのは知ってるんだけどね。

いきなり結論。

for /f "tokens=*" %d in ('"dir /b/s/a | findstr /r \.svn$"') do rmdir /q/s "%d"

ここにいたるまで若干の紆余曲折はあったんだけど、要点は以下。
  • dirコマンドには /aオプションもつける → .svnディレクトリが隠し属性だから。
  • .svnディレクトリだけ処理したいので、findstrで「\.svn$」という正規表現で限定してる。もっとうまいやり方あるかな。
  • dir → findstrとパイプでうまくつなげるには二重引用符でくくる。上のコマンドをよーく見てみて。
  • パスに空白が入る場合に対応して、/fの後にオプション「tokens=*」で1行すべてを1トークンとして認識させる
  • 同様に空白対策で、rmdirに渡すときに、%d自体を二重引用符でくくる
特にパイプ処理を通すためにコマンド自体を一重引用符の内側でさらに二重引用符でくくるってのはどうやって見つけたか忘れたけど結構ハマってたりして。

うん、こんな覚えづらいコマンドでちくちくするより「svn export --force work_dir target」したほうがよっぽど速いんだけども。

追記(08.9.7)

svn exportはおいておいて、dirコマンドのオプションでもう少し簡単になりました

ps.jsとps.cmd / kill.cmd2008年06月17日 00時54分14秒

なんちゃってコマンド 第四弾

我ながらしつこいなと思いつつ「なんちゃってコマンド」スクリプト。今度はps作ってみました。

ま、WMIでWin32_Process列挙できればそんなに難しくないので、似たようなことやってる人はいるだろなぁ。

例によってソース

ちょっと長いけど例によってのっけときます。NYSLです。

var fso = new ActiveXObject("Scripting.FileSystemObject");
var shell = new ActiveXObject("WScript.Shell");

// コマンドライン引数の取得
var args = new function() {
  var args = WSH.Arguments, result = [];
  for(var col = new Enumerator( args ); ! col.atEnd(); col.moveNext()) {
    result[ result.length ] = String( col.item() );
  }
  var named = {};
  for(var col = new Enumerator( args.Named ); ! col.atEnd(); col.moveNext()) {
    named[ String( col.item() ) ] = String( args.Named.Item( col.item() ) );
  }
  var unnamed = [];
  for(var col = new Enumerator( args.Unnamed ); ! col.atEnd(); col.moveNext()) {
    unnamed[ unnamed.length ] = String( col.item() );
  }
  result.named = named;
  result.unnamed = unnamed;
  
  result.toArray = function() {
    var result = [];
    for(var i = 0, l = this.length; i < l; i++) {
      result[ i ] = /((^".*"$)|(^'.*'$))/.test( this[i] ) ?
        this[i] : [ '"', this[i], '"' ].join("");
    }
    return result;
  };
  result.toString = function() {
    return this.toArray().join(" ");
  };
  return result;
}();

// cscriptで強制起動
if( /wscript\.exe$/i.test( WSH.FullName ) ) {
  shell.Run( [
    "cscript",
    /((^".*"$)|(^'.*'$))/.test( WSH.ScriptFullName ) ? WSH.ScriptFullName : [ '"', WSH.ScriptFullName, '"' ].join(""),
    args
  ].join(" ") );
  WSH.Quit();
}

// ユーティリティ関数定義
var echo = function(s) {
  print( [ s, "\n" ].join("") );
}
var print = function(s) {
  WSH.StdOut.Write( s || "" );
}
var input = function() {
  if( arguments[0] ) print( arguments[0] );
  print( ">" );
  return WSH.StdIn.ReadLine();
}

Error.prototype.toString = function() {
  return this.description || this.message || this.number || this;
};

var $break = {};
var $continue = {};
Enumerator.prototype.each = function(iterator) {
  try {
    var i = 0;
    for(this.moveFirst(); ! this.atEnd(); this.moveNext()) {
      try {
        iterator( this.item(), i++ );
      } catch(e) {
        if( e != $continue ) throw e;
      }
    }
  } catch(e) {
    if( e != $break ) throw e;
  }
};

String.prototype.repeat = function(count) {
  var buf = [];
  for(var i = 0; i < count; i++) buf[buf.length] = this;
  return buf.join("");
};
String.prototype.align = function(align, size) {
  if( ! /^((left)|(right)|(center))$/i.test( align ) ) align = "left";
  if( isNaN(size) || size < 1 ) size = this.length;
  switch( align ) {
  case "left":
    return [ this, " ".repeat(size) ].join("").substr(0, size);
  case "right":
    return [ " ".repeat(size), this ].join("").slice(-1 * size);
  default:
    var pad = " ".repeat( size );
    var s = [ pad, this, pad ].join("");
    return s.substr( parseInt(s.length / 2) - parseInt(size / 2), size );
  }
};
Number.prototype.toTime = function() {
  var sec = this % 60;
  var min = Math.floor( this / 60 );
  var hour = Math.floor( min / 60 );
  min = hour ? min % 60 : min;
  return [
    hour,
    ( "00" + min ).slice(-2),
    ( "00" + sec ).slice(-2)
  ].join(":");
};

var ProcMgr = function() {
  this.wmi = new ActiveXObject("WbemScripting.SWbemLocator").ConnectServer();
  var wshNet = new ActiveXObject("WScript.Network");
  this.currentUser = [ wshNet.UserDomain, wshNet.UserName ].join("\\");
};
ProcMgr.prototype = {
  list : function(id) {
    var query = "SELECT * FROM Win32_Process";
    query += id != null ? [ " WHERE ProcessId = ", id ].join("") : "";
    return new Enumerator( this.wmi.ExecQuery(query) );
  },
  kill : function(id) {
    this.list(id).each( function(proc) {
      proc.Terminate();
      throw $break;
    } );
  },
  exec : function(params) {
    if( params[0] && params[0].toLowerCase() == "--kill" && params[1] ) {
      // --kill
      this.kill( params[1] );
    } else if( params[0] && params[0].toLowerCase() == "--list" ) {
      // --list
      
      // -a オプション。全ユーザのプロセスを列挙
      var allUser = ( params[1] != null && /^-.*A/i.test( params[1] ) );
      // -d オプション。コマンドラインの情報を付加
      var detail = ( params[1] != null && /^-.*d/i.test( params[1] ) );
      
      var inited = false;
      var _self = this;
      this.list().each( function(proc) {
        if( ! inited ) {
          inited = true;
          echo( [
            "PID".align( "right", 5 ),
            "PPID".align( "right", 5 ),
            "OWNER".align( "left", 16 ),
            "WK-SIZE".align( "right", 8 ),
            "PEAK".align( "right", 8 ),
            "TIME".align( "right", 10 ),
            "CMD".align( "left", 16 )
          ].join(" ") );
        }
        var cmd = proc.CommandLine == null ? "(null)" : String( proc.CommandLine );
        var time = parseInt( ( Number(proc.KernelModeTime) + Number(proc.UserModeTime) ) / 10000 / 1000 );
        var info = proc.ExecMethod_( proc.Methods_.Item("GetOwner").Name, null );
        
        // プロセスオーナーのチェック
        if( ! allUser && [ info.Domain, info.User ].join("\\").toLowerCase() != _self.currentUser.toLowerCase() ) throw $continue;
        
        echo( [
          String(proc.ProcessId).align( "right", 5 ),
          String(proc.ParentProcessId).align( "right", 5 ),
          String(info.User || "(null)" ).align( "left", 16 ),
          String(Number(proc.WorkingSetSize) / 1024).align( "right", 8 ),
          String(Number(proc.PeakWorkingSetSize) / 1024).align( "right", 8 ),
          String( time.toTime() ).align( "right", 10 ),
          [
            String(proc.Name),
            detail ? [ "(", cmd, ")" ].join("") : ""
          ].join(" ")
        ].join(" ") );
      } );
    } else {
      // no parameter -> --list
      this.exec( [ "--list" ] );
    }
  }
};

new ProcMgr().exec( args );

今回のダウンロードはzip形式にしてます。こちらからどうぞ。

使い方

今回は不精して、1つのスクリプトで2つのコマンドを兼用しています。おかげでjsファイルとしての呼び出しはちと不自然な感じなんですが。

プロセスの列挙 - psコマンド風味

何も引数をつけずに起動すると psコマンドっぽく(?)稼動中のプロセスを列挙します。ただし、標準入力での入力待ちを行わないので、明示的にcscriptで起動したほうがよいでしょう。

C:\ps_command\scripts>cscript ps.js
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.

  PID  PPID OWNER             WK-SIZE     PEAK       TIME CMD
  472  1532 dara-j               2072     4912    0:01:01 tp4serv.exe
 2172  2112 dara-j              29992    95644    0:42:34 explorer.exe
 2272  2172 dara-j               4384     6808    0:00:30 TSVNCache.exe
    :
  (中略)
    :
 1640  2172 dara-j               2840     2884    0:00:00 cmd.exe
 4920  1640 dara-j               6612     6612    0:00:00 cscript.exe

C:\ps_command\scripts>

列の意味は、左から
  • PID - プロセスID
  • PPID - 親プロセスID
  • OWNER - プロセスのオーナー
  • WK-SIZE - ワーキングセット サイズ
  • PEAK - ピークメモリサイズ
  • TIME - CPU時間(カーネル+ユーザ)
  • CMD - コマンドイメージ名
といった具合です。まぁ「列の選択」で選べば普通にタスクマネージャで確認できる内容ばかりなんですが。

また、第一引数に「--list」をつけた場合も上と同じ動作になります。

C:\ps_command\scripts>cscript ps.js --list
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.

  PID  PPID OWNER             WK-SIZE     PEAK       TIME CMD
  472  1532 dara-j               2072     4912    0:01:01 tp4serv.exe
 2172  2112 dara-j              29928    95644    0:42:39 explorer.exe
 2272  2172 dara-j               4384     6808    0:00:30 TSVNCache.exe
    :
  (中略)
    :
 1640  2172 dara-j               2840     2884    0:00:00 cmd.exe
 5864  2172 dara-j               1888     6716    0:00:01 taskmgr.exe
 5212  1640 dara-j               6604     6604    0:00:00 cscript.exe

C:\ps_command\scripts>

--listで起動した場合はさらに第二引数にオプションをつけることができます。

「-a」オプションをつけると、他のオーナーが所有するプロセスも列挙します。といってもだいたい「SYSTEM」や「NETWORK SERVICE」くらいでしょうが。

C:\ps_command\scripts>cscript ps.js --list -a
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.

  PID  PPID OWNER             WK-SIZE     PEAK       TIME CMD
    0     0 (null)                 16        0   73:50:31 System Idle Process
    4     0 SYSTEM                 36     2040    0:05:51 System
  836     4 SYSTEM                 44      480    0:00:00 smss.exe
    :
  (中略)
    :
 1932  2172 dara-j               2848     2864    0:00:00 cmd.exe
 4696  1932 dara-j               6624     6624    0:00:00 cscript.exe
 3356  1168 NETWORK SERVICE      5728     5728    0:00:00 wmiprvse.exe

C:\ps_command\scripts>

これは個人的な趣味でつけたのですが、「-d」オプションをつけると、起動時のコマンドライン(引数まで込み)を出力します。

C:\ps_command\scripts>cscript ps.js --list -d
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.

  PID  PPID OWNER             WK-SIZE     PEAK       TIME CMD
  472  1532 dara-j               2072     4912    0:01:02 tp4serv.exe ("C:\Progr
am Files\Lenovo\TrackPoint\tp4serv.exe")
 2172  2112 dara-j              30244    95644    0:43:04 explorer.exe (C:\WINDO
WS\Explorer.EXE)
 2272  2172 dara-j               4408     6808    0:00:30 TSVNCache.exe ("C:\Pro
gram Files\TortoiseSVN\bin\TSVNCache.exe")
    :
  (中略)
    :
 5864  2172 dara-j               2632     6716    0:00:10 taskmgr.exe (C:\WINDOW
S\system32\taskmgr.exe)
 1932  2172 dara-j               5516     5592    0:00:00 cmd.exe ("cmd.exe" /K
cd C:\ps_command)
 2260  1932 dara-j               6624     6624    0:00:00 cscript.exe (cscript p
s.js --list -d)

C:\ps_command\scripts>

複数インスタンス起動できるエディタなんかの、あるインスタンスを特定する場合なんかに便利かなとか思ってつけたのですが、正直表示が見づらいです。まぁオマケ機能くらいの感じで。

実行例は示しませんが、「-a」と「-d」は「-ad」といった具合で同時に指定できます。

プロセスの強制終了 - killコマンド風味

第一引数に「--kill」をつけるとkillモードで起動します。この呼び出し方法は第二引数にプロセスIDを必ず指定する必要があります

以下はメモ帳(notepad.exe)を起動し、プロセスIDを確認後にkillモードで終了させる例です。

C:\ps_command\scripts>notepad

C:\ps_command\scripts>cscript ps.js | findstr notepad
 5728  1932 dara-j               4888     4888    0:00:00 notepad.exe

C:\ps_command\scripts>cscript ps.js --kill 5728
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.


C:\ps_command\scripts>cscript ps.js | findstr notepad

C:\ps_command\scripts>

バッチファイル

見てきたように2種類の動作を1つのスクリプトに集約したため、ちょっと引数が気持ち悪いのですが、バッチファイルを添付してあるので勘弁してください(ダウンロードファイルをzipにしたのはこのため)。

ダウンロードファイルを解凍すると、以下のような構造になっています。

  • ps_command/
    • scripts/
      • ps.js - スクリプト本体
    • kill.cmd - killモード起動用のバッチファイル
    • ps.cmd - psモード起動用のバッチファイル

バッチファイルはスクリプトパスを「"%~dP0\scripts\ps.js"」としているため、「ps_command」フォルダごとリロケータブルになっています。「%~dp0」がなんなのかはこちらのサイトなんかが参考になるのでは。

ps.cmdは見れば即わかるくらいシンプルですが、kill.cmdは引数なしの時にusageを表示する分岐処理を入れてます。まぁぜんぜん難しいことしてるわけではないのですが。

その他

なんというか、できそうだからやってみました、以上のことがないようなネタですが。本当はvmstatとかfreeとかもやろうかと思ったけど、まんまlinuxと同じ情報があるわけでなし、なにを出力するか考えるのも面倒になってきたのでなんちゃってシリーズはこれでおしまいかな。

ま、そうはいっても今回はCPU時間とかオーナー情報の取得でちとてこずったので、そこらへんを別記事にまとめる予定。