sudo.cmd / su.cmd ― 2015年10月18日 21時57分13秒
コマンドラインから管理者に昇格したい
たとえば、mklinkでシンボリックリンクを作りたい場合、いちいちcmd.exeを右クリックから「管理者として実行」するのは非常に面倒くさく、いま開いてるプロンプトから権限昇格できると便利だよね、と思って調べると、まぁいろいろな方々がすでにやってたりするわけです。
要は、WSHでShell.Application(エクスプローラを外部から操作するためのActiveXインターフェイス)で実行ファイルに対して「管理者として実行」で起動させる、という仕組みを使う、と。
んで、久々になんちゃってコマンド
んで、まぁ、suとかsudoとかを作ってみようかと。いや、先ほどのGoogle検索の結果に出てくるあちこちの記事でもすでに実現されてるんだけど、なんとなく自分でもやってみるか、という程度で。
suとsudoを別々に実装してもいいんだけど、cmd.exeを「管理者として実行」できればsu、任意のコマンドならsudoになるので、同じスクリプトを別のバッチファイルから呼び出す方式で実装することに。
ダウンロード
こちらからダウンロードをどうぞ。
zipには
- scripts/sudo.js
- su.cmd
- sudo.cmd
ソース - scripts/sudo.js
スクリプトのソースはこんな感じ。
キモは最後のほうにある「new ActiveXObject("Shell.Application").ShellExecute()」のところで、ここで第4引数に"runas"を与えると、エクスプローラのコンテキストメニューから「管理者として実行」を行ったことになる、という感じです。詳しくは以下のリンク先を参照。
Shell.ShellExecute method (Windows)
ソース - sudo.cmd
sudoのバッチはこんな内容です。
バッチに渡された引数をそのままsudo.jsに丸投げしてるだけの簡単なつくりです。
ソース - su.cmd
suのバッチはこんな感じ。
sudo.cmdでは引数をそのまま丸投げしてたところを、明示的に「cmd.exe /k」しています。
あと、スクリプトを実行すると新しいプロンプトが開くので、なにも考えずに使ってるとコマンドプロンプトのウィンドウだらけになってウザいので最後にexitしてますが、これはお好みで削除してもよいでしょう。
あ、それからsudo.js側で小技を入れているので、suした場合は元のディレクトリを引き継ぎます。
使い道とか
suしてmklinkしてシンボリックリンクをバンバン作成するとか、hostsいじるのにsudo notepadするとか、まぁそんな感じでしょうか。
PHPでWake-on-LANしてみたり、wol代替コマンドにしてみたり。 ― 2009年07月23日 05時48分39秒
WoL事情を調べてみた。なんとなく。
社内でちょっとした必要があって、Web経由でWake-on-LANによるPCの起動をするツールでも作ろうかと思って調べてみた。
Vineなんかはapt経由でwolコマンド(どうやらこちらのコマンドがオリジナルみたい)をさくっとインストールできたり、CentOSやDebianにもコマンドがあるらしいことがわかった。
これならPHP経由でもコマンド叩くだけで必要な機能は実装できそう(といいつつ、CentOSのethtoolはroot権限必要だけど)なので楽かなーとか思っていたが、Windowsでデバッグするのにちょっと面倒くさそう。
じゃ、PHPだけでやってみるか
PHPだけでなんとかできんものかと思って調べたら、すでにやっておられる方もいるので、自分でもやってみることにした。
WakeOnLan.php
こんな感じでやってみた。仮に「WakeOnLan.php」とでもしておく。
<?php class WakeOnLan { const BROADCAST_MAC_ADDR = 'FF:FF:FF:FF:FF:FF'; const DEFAULT_BROADCAST_IP = '255.255.255.255'; const DEFAULT_PORT = 2304; public static function macAddrToBytes($mac) { $mac = (string)$mac; if(! self::isValidMacAddr($mac)) { throw new Exception('invalid MAC address'); } $buf = array(); foreach(preg_split('/[:\-]/', $mac) as $one_octet) { $buf[] = chr(intval($one_octet, 16)); } return join('', $buf); } public static function isValidMacAddr($mac) { return preg_match('/^[\da-zA-Z]{2}([:\-][\da-zA-Z]{2}){5}$/', $mac); } protected $_broadcastIp; protected $_port; public function __construct($broadcastIp = null, $port = null) { $this->setBroadcastIp($broadcastIp)->setPort($port); } public function getBroadcastIp() { return $this->_broadcastIp; } public function setBroadcastIp($ip) { $ip = (string)$ip; if(empty($ip)) $ip = self::DEFAULT_BROADCAST_IP; $this->_broadcastIp = $ip; return $this; } public function getBroadcastUrl() { return 'udp://' . $this->getBroadcastIp(); } public function getPort() { return $this->_port; } public function setPort($port) { if($port == null) $port = -1; $port = (int)$port; if($port < 0) $port = self::DEFAULT_PORT; $this->_port = $port; return $this; } public function sendTo($mac) { $data = self::macAddrToBytes(self::BROADCAST_MAC_ADDR); $mac_data = self::macAddrToBytes($mac); for($i = 0; $i < 16; $i++) $data .= $mac_data; $fp = @fsockopen($this->getBroadcastUrl(), $this->getPort(), $errno, $errstr); if( $fp === false ) { throw new Exception($errstr . '(' . $errno . ')'); } fwrite($fp, $data); @fclose($fp); return $this; } }
ソース中に特にコメント入れてないけど、結構コンパクトなコードなので読むのはそんなに難しくないかと。WoL自体はWikipediaや@itの記事を見ればだいたいわかるし。
んで、使い方はかなり単純で、
- コンストラクタ(またはsetter)でブロードキャストアドレスとポートを設定し、
- sendToメソッドにターゲットのMACアドレスを渡す
たとえばMACアドレスが「00-0D-59-B5-31-08」なんてPCがあって、起動すると192.168.0.0/24なアドレスが割り振られるネットワークにいたとすると、これをWoLで起動する場合は
<php require_once 'WakeOnLan.php'; $wol = new WakeOnLan('192.168.0.255', 2304); $wol->sendTo('00-0D-59-B5-31-08');てな感じで使う。あ、MACアドレスはハイフン区切りでもコロン区切りでも同じ動作をします。
ほんとはもうちょっと簡単
先の例ではご丁寧にブロードキャストアドレス/ポートとも指定したが、WoL発行元と同一セグメント上のPCを起こすなら「255.255.255.255」(=リミテッド・ブロードキャスト・アドレスっていうらしい)で問題ないし、ポートについても気休め程度のものらしいので、コンストラクタ引数を省略して
$wol = new WakeOnlan();だけでもよい。
で、バッチで包む
例であげたような起動用PHPを作成してもいいんだけど、引数をそのまま流し込めばいいくらいなので、こんなバッチファイルで済ませられる。
@echo off php -r "require_once 'WakeOnLan.php'; $wol=new WakeOnLan('%2'); $wol->sendTo('%1');"これを「wol.bat」とか「wol.cmd」とかって名前でパスが通った場所に保存して(WakeOnLan.phpはinclude_pathが設定されているところにおいておけばいいでしょ)、
wol 00:0D:59:B5:31:08とか、
wol 00:0D:59:B5:31:08 192.168.0.255なんて感じで叩いたり、1行 - 1MACアドレスなテキストファイル作って、
for %m in ('type macaddr.txt') do @wol %mみたいな感じで一斉起動とか。
余談
WakeOnLan.phpではMagicPacketを送出するのにfsockopen() → fwrite()だけで済ませてるけど、参考にさせてもらったこちらのコードではsocket_set_option()でブロードキャスト許可設定をしている。fsockopen()の場合にはいらないんだろうか。それともデフォルトでブロードキャスト許可されてるんだろうか。
ZendFrameworkのライブラリやドキュメントをコマンドラインからダウンロードするバッチファイル ― 2009年06月10日 04時18分31秒
なんとなく作ってみました
いやー、我ながら毎度毎度くだらんと思うんだけど、Zend Frameworkのアーカイブやらドキュメントやらをコマンドラインでダウンロードするバッチを作ってみたり。ソースも載せてますが、こちらからダウンロードもできます。
元々は、ローカルのsvnリポジトリに、ZFの各リリース版を上書き→コミットしてバージョン間の差分を見てみようというくだらない思いつきが発端なんだけども、アーカイブページからちくちくとリンククリックしたりするのが面倒になってきたもので。いや、ダウンローダ使えばいいんだけどね。
バッチは難しいや
ソースはこんな感じ。
@echo off set VER=%1 set EXT=%2 set KIND=%3 if "%EXT%"=="" goto setext else goto setkind :setext set EXT=zip :setkind if "%KIND%"=="doc" goto setman goto setlib :setman set KIND=-manual-ja set MSG=マニュアル goto dl :setlib set KIND= set MSG=ライブラリ :dl @echo Zend Framework %VER% の %MSG% をダウンロードします wget http://framework.zend.com/releases/ZendFramework-%VER%/ZendFramework-%VER%%KIND%.%EXT%ファイル名は「zfdl.bat」とか「zfdl.cmd」とか、適当に。
しかしバッチ書き慣れないせいか、えらく時間かかったうえにかなりグダグダしたソースだなぁ。難しいや。
あ、これ実行するには拙作のwget.js(+ラッパーのバッチファイル)が必要です。ラッパー書くのめんどくさい場合は最終行の
wget http:.....を
cscript //nologo wget.js http://....のように直接JS起動にしても大丈夫ですが。
使い方
引数を1~3つとります。
最初の引数はダウンロード対象のZFのバージョンを「x.x.x」形式で指定します。たとえば、ZF1.0.0を対象とする場合は、
zfdl 1.0.0のように指定します。
第二引数はオプションで、ダウンロードするファイルの拡張子を指定します。指定可能な拡張子は「.tar.gz」または「.zip」の2種類のみで、省略時、または他の拡張子などを指定した場合は「.zip」になります。先頭にピリオドが必要な点に注意。
第三引数もオプションで、これに「doc」と指定するとドキュメントをダウンロードします。省略時または「doc」以外のパラメータを指定した場合はライブラリのアーカイブをダウンロードします。
コマンドのサンプル
例1:1.8.1のライブラリを.zipでダウンロード
zfdl 1.8.1
例2:1.6.2のライブラリを.tar.gzでダウンロード
zfdl 1.6.2 .tar.gz
例3:1.7.8のドキュメントを.tar.gzでダウンロード
zfdl 1.7.8 .tar.gz doc
例4:1.0.0のドキュメントを.zipでダウンロード
zfdl 1.0.0 "" doc第二引数は二重引用符が2つ重なっているだけです。
forコマンドで連続ダウンロード
まぁ、そんな需要はまずないと思うのですが、forコマンドで指定バージョンを自動的に連続ダウンロードすることもできます。
まず落としたいバージョンを列挙した、以下のような感じのテキストファイルを用意しておきます。
1.0.0 1.0.1 1.0.2 1.0.3 1.0.41行に1つのバージョンを記述する形式です。
で、これをforコマンドのタネにします。たとえば上記のファイルを「ver.txt」として、.zipでマニュアルをダウンロードするには
for /f %v in ('type ver.txt') do @zfdl %v .tar.gz docのようにします。
バッチファイルそのものにもう少し手を入れれば、ダウンロード済みのファイルは落とさないようにしておいて、勝手に最新版をダウンロードするようにもできるような気もしますが、まぁそんな必要はないか。
追記
あれ?拡張子の先頭のピリオド、いらないじゃん。
さらに追記
えと、元々wget.jsの作りからして、ダウンロードする内容をいったんすべてメモリに蓄えるので、ドキュメントのダウンロードに使い方絞ったほうが安全かもしれません(dara-jの環境では1.8.1をzip形式でDLできましたが)。
ついでにいうと、圧縮率考えると、zipよりもtar.gzのほうがお勧めです。
.wmiコマンドの説明 ― 2008年10月21日 04時42分51秒
昨日の続きで、.wmiコマンドの説明でもしようかと。
起動
jsi.js環境でWQLを直接実行するための追加ドットコマンドで、jsi.jsのプロンプトから「.wmi」と入力することで起動します。
昨日の記事でも例示していますが、.wmiを起動すると、モードが通常のJSモードからWQLモードに切り替わり、プロンプト表示も変化します。
Microsoft (R) Windows Script Host Version 5.6 Copyright (C) Microsoft Corporation 1996-2001. All rights reserved. jsi > .wmi WMI Console for jsi.js server: localhost, root: root/cimv2 wmi@localhost >
.wmiコマンドには、接続パラメータを指定することもできます。指定したパラメータはWbemScripting.SWbemLocatorオブジェクトのConnectServerメソッドの第一~第四引数にそのままそのまま渡されます。具体的には以下の通り。
- strServer
→ ログインするコンピュータの名前を指定します。省略時は「.」、すなわちローカルホストになります。 - strNamespace
→ ログインする名前空間を指定します。本来は省略も可能ですが、パラメータ展開の都合で第三パラメータ以降の指定が必要な場合は省略できません。その場合はシステム標準の名前空間「root/cimv2」を指定してください。 - strUser
→ strServer, strNamespaceで指定されたコンピュータ・名前空間へログインするためのユーザ名を指定します。省略時は現在のセキュリティコンテキストで使用されているログイン名が使用されます。 - strPassword
→ strServer, strNamespaceで指定されたコンピュータ・名前空間へログインするためのパスワードを指定します。省略時は現在のセキュリティコンテキストで使用されているログインパスワードが使用されます。
リモートコンピュータ「myserver」の名前空間「root/cimv2」へアカウント「username:password」で接続するには以下のように指定します。
jsi > .wmi myserver root/cimv2 username password WMI Console for jsi.js server: myserver, root: root/cimv2 wmi@myserver >
WQLの実行
WQLモードでは、後述のドットコマンドを除いて、WQL以外のコマンドは実行できません。
入力方法はJSモードと同様セミコロンが出現するまでは入力モードです。末尾にセミコロンを入力することで初めてWQLによる検索を実行します。
wmi@localhost > select name, creationdate from win32_process; // 検索結果がダンプされる出力はデフォルトではJSON形式ですが、後述のドットコマンド「.format」でカンマ区切りまたはタブ区切りのテキストへ変更することもできます。
また、標準ではコンソールへのみ出力されますが、後述のドットコマンド「.output」でファイルへの出力も指定できます。
WQLモードを終了する場合はドットコマンド「.quit」を入力します。.quitを実行するとJSモードへ戻ります。
wmi@localhost > .quit jsi >
ドットコマンド「.format」
ドットコマンド「.format」で出力フォーマットの確認・変更が行えます。
パラメータを指定せずに.formatを実行した場合は現在の出力フォーマットが表示されます。
wmi@localhost > .format - json-format wmi@localhost >パラメータ指定を行う場合は".format"に続けて演算子"="と有効なフォーマット名を指定します。
wmi@localhost > .format = json - json-format wmi@localhost > .format = csv - csv-format wmi@localhost >
.formatで指定可能なフォーマット名は以下の3種類です。
- "json"
→ JSON形式で出力します。ただし、1行分のデータのみが正しいJSON形式で、複数データがヒットした場合でも各データ間はカンマで区切られないので注意してください(表示のみなら特に問題なし)。 - "csv"
→ CSV(カンマ区切りテキスト)形式で出力します。先頭行はカラムヘッダ、それ以降の行に結果がカンマ区切りで出力されます。 - "tsv"
→ TSV(タブ区切りテキスト)形式で出力します。CSVフォーマット同様、先頭行がカラムヘッダ、それ以降が結果データになります。
ドットコマンド「.output」
ドットコマンド「.output」を使用することで、検索結果が出力されるストリームを確認・構成することができます。
現在の出力ストリーム構成を確認するには、パラメータ指定なしで「.out」または「.output」と入力します。
wmi@localhost > .out - [0]: (standard output stream) wmi@localhost >
ストリームの追加
デフォルトの状態では標準出力のみがストリームとして登録された状態です。以下のように、「+=」を使用してファイルストリームを追加することができます。
wmi@localhost > .out += log.txt - [0]: (standard output stream) - [1]: file stream:[log.txt] wmi@localhost >ファイルのパスはjsi.jsのインストールディレクトリからの相対パスまたは絶対パスで指定できます。絶対パス指定する場合は、パスを二重引用符で括らないでください。
上記の構成では、標準出力および.jsi.jsのインストールディレクトリ直下のlog.txtに結果が出力されます。
ストリームの削除
追加したストリームを構成から削除するには「-=」に続けてファイル名/パスを入力してください。
wmi@localhost > .out - [0]: (standard output stream) - [1]: file stream:[log.txt] wmi@localhost > .out -= log.txt - [0]: (standard output stream) wmi@localhost >
現在のバージョンではまれにストリームが削除できなくなる場合があります。いずれ修正予定ですが、このような状態になったらいったんjsi.jsを終了してください。
まとめ
というように、WQLを実行して結果を表示するだけのコマンドで、検索結果に対してなにか処理できるわけでもないので実用性はあまりありません。
ま、リモート接続ができるので、たとえばCSVフォーマットでリモートコンピュータにインストールされているプリンタを列挙したりとか、多少は役に立つかもしれませんが、WMIのクラスリファレンス見ながら「へぇ、こんなクラスあるのかー。どんな情報取れるんだろ」を試すくらいしか使い道ないかもしれません。
またしても長めの記事になっちゃったので、ライブラリ自体の説明はまた後日。
jsi.jsに手を加えてみたり。 ― 2008年10月20日 04時33分42秒
最近なんだかネタになるようなスクリプトとか書いてる時間がぜんぜんなくてネタにこまったのでjsi.jsに手を入れてみた。
といっても、コードの修正とかは結構前にやっていたもので、どちらかというと蔵出し的な感じ。ほんとにネタに困ってるんだってば。
手を入れたところは細かいバグ修正とライブラリの追加。zipは固めなおしてリビジョンあげたので、以下からダウンロードしてください。
サンプルのライブラリ設定ファイルとして、付属ライブラリをすべてロードする設定で「libsettings.sample」を添付しているので、これを「libsettings」とリネーム、適当に編集して使ってください。
jsi.jsの変更内容
こんな感じ。
- ドットコマンドの引数展開バグの修正
- ライブラリロード中の例外発生時に、ロード中ライブラリファイル名も表示するよう改善
- ドットコマンド「.wmi」を追加
- → 後述の追加ライブラリ「wmi_console.js」を必要とします。注意。
追加ライブラリ・vbscript.js
かなり前のネタになるが、VBScriptラッパーをライブラリに追加。細かい使い方はリンク先を見てもらうとして、いくつか組み込みメソッドを提供しているのでちょろっと使えるかも。ま、こっちの記事ですでに紹介してるやつなんですが。
getTypeNameメソッド
VBSのTypeName関数のラッパーです。引数に渡したオブジェクトのCOMオブジェクト型を返します。こんな感じ。
jsi > var vb = new VBScript; --> (undefined) jsi > vb.getTypeName(new ActiveXObject("Scripting.FileSystemObject")); --> FileSystemObject jsi >
promptメソッド
VBSのInputBox関数のラッパー。ブラウザのprompt()感覚で使える。かな。
引数は2つ渡すことが可能で、第一引数にはダイアログメッセージ、第二引数にダイアログタイトルを指定できます。んで、入力された文字列か、キャンセルされた場合はnull(正確にはundefined)が返ります。
jsi > vb.prompt(); // ここでInputBoxが表示される。キャンセルしたとすると... --> (undefined) jsi > vb.prompt(); // こんどは'hoge'とでも入れてみると... --> hoge jsi >
yesNoメソッド
VBSのMsgBoxラッパーで、vbYesNo定数を渡して[はい][いいえ]の2択処理をします。引数はpromptメソッドと同じくダイアログメッセージとタイトルです。戻り値はvbYes定数(=6)かvbNo定数(=7)が返ります。もう一層ラッパーかぶせてconfirmっぽくしてもよいかも。
jsi > vb.yesNo(); // [はい]クリック --> 6 jsi > vb.yesNo(); // こんどは[いいえ]クリック --> 7 jsi >
getNothingとgetEmpty
まず役立つ局面はそうはないだろうけど、VBSでのNothingを取得するgetNothingメソッドもつけてます。JS上ではnullとして扱われますが、getTypeName()を通すとNothingであることが確認できます。それだけですが...
jsi > vb.getNothing(); --> (null) jsi > vb.getTypeName(vb.getNothing()); --> Nothing jsi > vb.getTypeName(null); --> Null // JSのnullはNullあと、ほとんど意味ないんですが、getEmpty()もつけてみました。VBSのEmptyを取得します。が、これ、JSのundefinedと同じなのよね。サンプル省略。
追加ライブラリ・wmi_console.js
WMIへアクセスするためのライブラリ+WQLを実行するコンソール機能を提供します。詳しい解説はそのうち書きますが、とりあえず追加のドットコマンド「.wmi」を実行してみてください。
jsi > .wmi WMI Console for jsi.js server: localhost, root: root/cimv2 wmi@localhost >プロンプトがこのように変わります。この状態でWQLを入力してみます。
wmi@localhost > select name, creationdate from win32_process; { "CreationDate" : null, "Handle" : "0", "Name" : "System Idle Process" } : : { "CreationDate" : "2008/10/20 03:33:09", "Handle" : "10008", "Name" : "sakura.exe" } { "CreationDate" : "2008/10/20 04:28:48", "Handle" : "9224", "Name" : "wmiprvse.exe" } wmi@localhost >こんな感じでWQLの実行結果が表示されます。元のJSシェルに戻るには.quitしてください。
思ったより記事が長くなっちゃったので、この.wmiコマンドとライブラリ自体の説明はまた後日。
最近のコメント