Diary on wind(跡地)

おもにPC関係(2008-2013)、跡地兼日記避難所(2014-)

広告

※このエリアは、60日間投稿が無い場合に表示されます。記事を投稿すると、表示されなくなります。

Windowsのバージョンや32/64ビットで分岐 [コマンドプロンプト]

2012-05-29 23:01:30 | Winコマンド

以前使ったVERコマンドではDOSからWindows 7まで幅広いバージョンで利用できたわけですが、Windows Vista以降では完全なOS名が出てこなくなりました。バージョン番号で大まかな判別はできましたが、Windows VistaとWindows Server 2008が同じバージョン番号であり区別ができません。また32ビット/64ビットの判別もできません。
SYSTEMINFOコマンドを使えばOS名や32/64ビットの情報を取得できますが、その結果をFORコマンドのdelimsスイッチで取得するのは、今後の互換性を考えるとあまり望ましくありません。

そこで今回はWMICコマンドを使ってWMIを通じて情報を取得してみます。
WMICコマンドはWindows XP以降で使えます。ただしWindows XP Home EditionにはWMICコマンドは搭載されていないので、XPを動作対象に入れる場合は注意が必要です。

SETLOCAL
REM WMICコマンドでシステム情報を取得し変数に代入
FOR /F "tokens=*" %%A IN ('WMIC OS Get ServicePackMajorVersion^,BuildNumber^,Caption /Value ^| FIND "="') DO (
SET OS.%%A
)
FOR /F "tokens=*" %%A IN ('WMIC CPU Get AddressWidth /Value ^| FIND "="') DO (
SET CPU.%%A
)
REM OS名で分岐
ECHO %OS.CAPTION%|FIND "Windows 7">NUL
IF %ERRORLEVEL% EQU 0 (
IF %OS.ServicePackMajorVersion% GEQ 1 (
ECHO お使いのオペレーティングシステムはWindows 7 Service Pack 1以降です。
) ELSE (
ECHO お使いのオペレーティングシステムはWindows 7 RTMです。
) )
ECHO %OS.Caption%|FIND "Windows ServerR 2008">NUL
IF %ERRORLEVEL% EQU 0 (
ECHO お使いのオペレーティングシステムはWindows Server 2008です。
)
ECHO %OS.Caption%|FIND "Windows Vista">NUL
IF %ERRORLEVEL% EQU 0 (
ECHO お使いのオペレーティングシステムはWindows Vistaです。
)
REM ビルド番号で分岐
IF %OS.BuildNumber% GEQ 6000 (
ECHO お使いのオペレーティングシステムのビルド番号は6000以降です。
)
REM Windowsの32ビット/64ビットで分岐
IF %CPU.AddressWidth% EQU 32 (
ECHO お使いのオペレーティングシステムは32ビット版です。
) ELSE (
ECHO お使いのオペレーティングシステムは64ビット版です。
REM コマンドプロンプトの32ビット/64ビットで分岐
IF "%PROCESSOR_ARCHITECTURE%"=="x86" (
ECHO コマンドプロンプトは32ビット^(WOW64^)で動作しています。
) ELSE IF "%PROCESSOR_ARCHITECTURE%"=="AMD64" (
ECHO コマンドプロンプトは64ビットで動作しています。
) )
ENDLOCAL

個々のコマンドについてはいちいち説明しません。分岐はVistaと7しか入れていないので、適宜追加して下さい。
OS名からはエディションの判別もできますが、個人的にはあまりお勧めできません。例えばWindows XP Media Center Editionでは「Windows XP Professional」として返ってきます。
IF文の組み方は見やすいように・理解しやすいように臨機応変に。

最後の「コマンドプロンプトの32ビット/64ビット」について補足しておきます。
64ビット版Windowsのコマンドプロンプト(cmd.exe)には32ビット版(%windir%\syswow64\cmd.exe)と64ビット版(%windir%\System32\cmd.exe)が存在します。通常、単体でコマンドプロンプトを実行すると起動するのは64ビット版の方ですが、WOW64下で動作する32ビットアプリケーションからコマンドプロンプトを呼び出すと32ビット版の方が起動します。

試しにCで下のようなプログラムを作ったとしましょう。

#include "stdlib.h"
main()
{
 	system("pause");
}

標準ライブラリ(msvcrt)のsystem関数を通じてコマンドプロンプトのPAUSEコマンドを実行するだけのプログラムです。
これを32ビット版コンパイラでコンパイル・実行した場合

C>cl a.c
Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

a.c
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:a.exe
a.obj

C>a
続行するには何かキーを押してください . . .

C>

これを64ビット版コンパイラでコンパイル・実行した場合

C>cl a.c
Microsoft(R) C/C++ Optimizing Compiler Version 15.00.30729.01 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

a.c
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:a.exe
a.obj

C>a
続行するには何かキーを押してください . . .

C>

アプリケーションの実行中はコマンドプロンプトの画面を見ただけでは32ビット/64ビットの区別はできません。
実行前のタスクマネージャーのプロセス一覧

32ビット版を実行中

64ビット版を実行中

32ビットアプリケーションからは32ビット版、64ビットアプリケーションからは64ビット版のコマンドプロンプトが起動されていることがわかります。

○更新履歴

  • 2012/06/07 WinServer2008追加 
  • 2012/05/29 作成

指定期間のイベントログを取得 [PowerShell]

2011-12-07 23:13:11 | Winコマンド

Get-WinEventコマンドレットにおいて、フィルターで日時を指定してイベントログを絞り込む方法。

日時で絞り込む方法はGet-WinEventコマンドのヘルプに載っています。なのでわざわざ取り上げるような話ではないと思っていましたが、話題として全く取り上げられていない≒あまり知られていないようなので、少し触れてみることにします。ところで一言付け加えておくと、私はITを専門・職業とする技術者ではなく単に個人で使い込んでいるだけなので、このサイトの情報をシステム開発・管理などの業務に利用しようと考えている方は注意して下さい。

Get-WinEventコマンドで日時フィルターを指定する方法は主に3通りに分けられます。

  1. コマンドで返されるオブジェクトの型がEventLogRecordクラスなので、そのTimeCreatedプロパティ(DateTime型)と比較する方法。
  2. ハッシュテーブルのクエリでStartTimeキー、EndTimeキーを指定する。
  3. XPathのフィルタークエリでTimeCreated要素の比較やtimediff関数(非標準)を利用する。

1の方法の場合はまずGet-WinEventコマンドを実行してから、Where-ObjectコマンドでTimeCreatedプロパティの条件を指定してパイプで繋ぐことになります。この方法は、Get-WinEventコマンドで取得するイベントログが数万件もあると実行に時間が掛かってしまいます。2の方法が効率がよく、わかりやすいスクリプトになるのでお勧めです。ただ、複雑な条件を指定できないという点ではXPathに劣ります。3の方法では複雑な条件を指定できる代わりにやや読みにくい(難解な)スクリプトになります。

○例1 過去3日間のSystemログのイベントログを取得する

方法はGet-WinEventコマンドのヘルプの例14とほぼ同じです。行頭のPSは省いてください。

# 方法1 (注:場合によっては処理が終わるまでにかなり時間がかかります。)
PS $startdate = (get-date) - (new-timespan -day 3)
PS get-winevent -logname "System" | where {$_.timecreated -ge $startdate}

# 方法2
PS $startdate = (get-date) - (new-timespan -day 3)
PS get-winevent -FilterHashTable @{LogName='System'; StartTime=$startdate}

# 方法3
PS get-winevent -LogName "System" -FilterXPath "*[System[TimeCreated[timediff(@SystemTime) <= 259200000]]]"

方法2はWindows Vistaで実行すると"パラメータが間違っています。"というエラーが返ってきます。これはバグらしく、Windows 7では修正されたためちゃんと実行できます。
方法3ではtimediff関数を使っています。これはWindowsのイベントログのXPathフィルター特有の関数であり、XPathの標準仕様にはありません。timediff関数でTimeCreatedとSystemTimeの差を出して、それが259200000(ミリ秒)=3日以内、というように条件を指定しています。

○例2 2011年12月1日から同月7日までの間の全ユーザーのログオン履歴を取得する

# 方法1 (注:場合によっては処理が終わるまでにかなり時間がかかります。)
PS $timeoffset = new-object System.TimeSpan 9,0,0
PS $starttime = new-object System.DateTimeOffset 2011,12,1,0,0,0,0,$timeoffset
PS $endtime = new-object System.DateTimeOffset 2011,12,7,23,59,59,999,$timeoffset
PS get-winevent -logname "Security" | where {($_.ProviderName -eq 'Microsoft-Windows-Security-Auditing') -and ($_.id -eq 4624) -and ($_.timecreated -ge $starttime.LocalDateTime) -and ($_.timecreated -le $endtime.LocalDateTime)}

# 方法2
PS $timeoffset = new-object System.TimeSpan 9,0,0
PS $starttime = new-object System.DateTimeOffset 2011,12,1,0,0,0,0,$timeoffset
PS $endtime = new-object System.DateTimeOffset 2011,12,7,23,59,59,999,$timeoffset
PS get-winevent -FilterHashTable @{LogName='Security'; ProviderName='Microsoft-Windows-Security-Auditing'; ID=4624; StartTime=$starttime.LocalDateTime; EndTime=$endtime.LocalDateTime}

# 方法3
PS get-winevent -logname Security -filterxpath "*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and (EventID=4624) and TimeCreated[@SystemTime>='2011-11-30T15:00:00.000Z' and @SystemTime<='2011-12-07T14:59:59.999Z']]]"

以前の記事で述べたとおり、Securityログへのアクセスには(たぶん)管理者権限が必要です。

方法1、方法2について。$timeoffsetにはこれから指定する日時とUTCとの時差を設定し、$starttime、$endtimeで日本時間で日時を設定しています。get-wineventコマンド実行時にはLocalDateTimeプロパティで現地日時に変換します。(タイムゾーンが日本に設定されている場合はそのまま。)まあ普通に使っている分にはこんな回りくどいことしなくても、初めからDateTime型で指定すればいいのですが。

方法3では日時をUTCで指定する必要があるので注意して下さい。XPathがよくわからない or 考えるのが面倒ならイベントビューアーでXPathのフィルタークエリを作成するといいでしょう。

現在のログをフィルタ - イベントビューアー
現在のログをフィルタ(XML)

FilterXPathスイッチで指定するときはSelectタグで囲まれた部分だけをコピーして持ってきます。FilterXmlスイッチで指定するときは全てをコピーします。今まで紹介してきませんでしたが、Get-WinEventコマンドのフィルターの指定方法はFilterXPath、FilterHashTableの他にFilterXMLがあります。FilterXMLでは複数のXPathを使って複雑なクエリを作成することができますが、それほど使う機会はないでしょう。


イベントログから起動・終了履歴を取得 [PowerShell]

2011-11-22 22:17:29 | Winコマンド

PowerShellでWindows起動・終了についてのログを取得。

○対象とする動作環境と必要事項

  • Windows Vista、7、Windows Server 2008 R2
  • .net Framework 3.5以降
  • Windows PowerShell 2.0以降

この前紹介したログオン履歴の取得には管理者権限が必要でしたが、今回は管理者権限は不要です。

○ソース"EventLog"のイベントID 6005と6006

Windows Vistaと7で使える方法です。Windows Serverはよくわかりません。
イベントID 6005が起動、6006が終了のログです。

$ get-winevent System -filterxpath "*[System[Provider[@Name='EventLog'] and (EventID='6005' or EventID='6006')]]" -maxevents 10
TimeCreated            ProviderName        Id Message
-----------            ------------        -- -------
2011/11/22 2:43:20     EventLog          6005 イベント ログ サービスが開...
2011/11/22 2:41:43     EventLog          6006 イベント ログ サービスが停...
2011/11/18 18:03:32    EventLog          6005 イベント ログ サービスが開...
2011/11/18 18:02:08    EventLog          6006 イベント ログ サービスが停...
2011/11/14 21:09:23    EventLog          6005 イベント ログ サービスが開...
2011/11/14 21:08:30    EventLog          6006 イベント ログ サービスが停...
2011/11/14 20:31:45    EventLog          6005 イベント ログ サービスが開...
2011/11/14 20:29:52    EventLog          6006 イベント ログ サービスが停...
2011/11/09 18:58:40    EventLog          6005 イベント ログ サービスが開...
2011/11/09 18:50:50    EventLog          6006 イベント ログ サービスが停...

"-filterxpath"スイッチでXPathを使ってフィルタークエリを記述しています。また"-maxevents"スイッチでログを最近10件のみ取得するように指定しています。以前やったことと同じなので細かい説明は省きます。
クエリの中でソース名やイベントIDをシングルクォーテーションで囲んでいますが、この場合はなくても正しく実行できます。

○ソース"Kernel-General"のイベントID 12と13

新たにWindows 7で記録されるようになったログを利用しています。なのでたぶんWindows Vistaでは使えません。
イベントID 12が起動、13が終了のログです。これでWindows 7の場合、今まで紹介しただけでも3種類のイベントログから起動ログを取れるわけですが、どれを取るかは各々の判断にお任せします。

$ get-winevent System -filterxpath "*[System[Provider[@Name='Microsoft-Windows-Kernel-General'] and (EventID='12' or EventID='13')]]" -maxevents 10
TimeCreated            ProviderName                        Id Message
-----------            ------------                        -- -------
2011/11/22 21:04:14    Microsoft-Windows-Kernel-G...       12 オペレーティング システム...
2011/11/22 18:57:26    Microsoft-Windows-Kernel-G...       13 オペレーティング システム...
2011/11/22 18:43:43    Microsoft-Windows-Kernel-G...       12 オペレーティング システム...
2011/11/22 18:41:49    Microsoft-Windows-Kernel-G...       13 オペレーティング システム...
2011/11/22 18:31:43    Microsoft-Windows-Kernel-G...       12 オペレーティング システム...
2011/11/22 18:29:29    Microsoft-Windows-Kernel-G...       13 オペレーティング システム...
2011/11/22 14:09:19    Microsoft-Windows-Kernel-G...       12 オペレーティング システム...
2011/11/22 6:28:07     Microsoft-Windows-Kernel-G...       13 オペレーティング システム...
2011/11/22 6:20:25     Microsoft-Windows-Kernel-G...       12 オペレーティング システム...
2011/11/22 5:00:07     Microsoft-Windows-Kernel-G...       13 オペレーティング システム...

format-tableコマンドと繋いで出力形式を変えると少し見易くなります。ただしここで出てくる日時は日本時間ではなくUTCです。

$ get-winevent System -filterxpath "*[System[Provider[@Name='Microsoft-Windows-Kernel-General'] and (EventID='12' or EventID='13')]]" -maxevents 10|format-table Message
Message
-------
オペレーティング システムはシステム時刻 ‎2011‎-‎11‎-‎22T12:04:14.125599300Z に開始しました。
オペレーティング システムはシステム時刻 ‎2011‎-‎11‎-‎22T09:57:26.643706400Z にシャット ダウンします。
オペレーティング システムはシステム時刻 ‎2011‎-‎11‎-‎22T09:43:43.125599300Z に開始しました。
オペレーティング システムはシステム時刻 ‎2011‎-‎11‎-‎22T09:41:49.235925100Z にシャット ダウンします。
オペレーティング システムはシステム時刻 ‎2011‎-‎11‎-‎22T09:31:43.125599300Z に開始しました。
オペレーティング システムはシステム時刻 ‎2011‎-‎11‎-‎22T09:29:29.321243500Z にシャット ダウンします。
オペレーティング システムはシステム時刻 ‎2011‎-‎11‎-‎22T05:09:19.125599300Z に開始しました。
オペレーティング システムはシステム時刻 ‎2011‎-‎11‎-‎21T21:28:07.489708900Z にシャット ダウンします。
オペレーティング システムはシステム時刻 ‎2011‎-‎11‎-‎21T21:20:25.125599300Z に開始しました。
オペレーティング システムはシステム時刻 ‎2011‎-‎11‎-‎21T20:00:07.877281400Z にシャット ダウンします。

ファイルに書き出す方法は以前に紹介したとおりです。

○更新履歴

  • 2011/11/22 作成

イベントログをファイルに書き出す [PowerShell]

2011-11-17 06:40:47 | Winコマンド

PowerShellでイベントログのデータをファイルにエクスポート(書き出す)方法。

○必要動作環境

  • Windows Vista、7、Windows Server 2008 R2
  • .NET Framework 3.5以降
  • Windows PowerShell 2.0以降

○イベントログアーカイブファイル(*.evtx)形式でエクスポート

今回はWindows Vista以降の新形式のイベントログに対応したコマンド・クラスを使おうと思います。Windows XPでは利用できません。
ところがPowerShellの中で新形式のログに対応したコマンドレットはGet-WinEventコマンドしかありません。このコマンドはログの取得のみ可能で、ログのデータを保存することはできません。そこで今回は、やや強引ではありますが、.NET Framework 3.5で追加されたクラスライブラリ(System.Diagnostics.Eventing.Reader.EventLogSessionクラス)のExportLogメソッドを利用することにします。

#パラメーター定義
$logname = "System"
$logquery = "*[System[Provider[(@Name='Microsoft-Windows-Kernel-Power')] and (EventID='42')]]"
$outfile = $env:USERPROFILE + "\Desktop\System.evtx"
#ここからスクリプト
if(Test-Path -Path $outfile) {
Remove-Item $outfile
}

$evsession
= New-Object -TypeName System.Diagnostics.Eventing.Reader.EventLogSession
$evsession.ExportLog($logname,"LogName",$logquery,$outfile)

上のサンプルではSystemログからソースがMicrosoft-Windows-Kernel-PowerかつイベントIDが42のイベントを抽出し、現在のユーザーのデスクトップフォルダーにSystem.evtxとしてエクスポートしています。$logname、$logquery、$outfileは適当に変えてください。ExportLogメソッドではファイルの上書きはできません。前もってファイルの存在を確認して削除するか処理を中断するのがいいでしょう。
出力ファイルの拡張子は.evtxというもので、イベントビューア等で閲覧することができます。PowerShellではGet-WinEventコマンドでこの形式のファイルを扱うことができます。
ExportLogメソッドの代わりにExportLogAndMessagesメソッドを用いると表示情報も一緒に出力することができます。その場合出力先にLocaleMetaDataフォルダーが作成されます。ただ、その際にPowerShellが管理者権限でないとエラーになることがあります。
すべてのイベントを名前をつけて保存 - イベントビューアー

○XML(*.xml)形式でエクスポート

.evtx形式はイベントビューアや.NET Frameworkアプリケーションで利用する分には便利ですが、中身はテキストではないので.NET Frameworkを利用できない環境で扱うのは困難です。そこで新形式のイベントログの特長を生かして、ログデータをXML形式でエクスポートしようという作戦です。

$events = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?><Events>"
foreach($eventnode in Get-WinEvent System -MaxEvents 1000) {
$events += $eventnode.ToXml()
}
$events += "</Events>"
$events | Out-File ($env:USERPROFILE + "\Desktop\System.xml") -Encoding utf8

上のサンプルはSystemログの最近1000件のログをデスクトップにSystem.xmlとして保存しています。Get-WinEventコマンドとToXmlメソッド以外は特別な操作はしていません。出力するXMLのフォーマットはイベントビューアの保存形式に準じています。以前に紹介したようにGet-WinEventコマンドにFilterHashTable、FilterXML、FilterXPathのいずれかのパラメーターでクエリを指定すればフィルターをかけることができます。

○更新履歴

  • 2011/11/23 細かいミスがあったので修正
  • 2011/11/17 作成

イベントログから起動・ログオン履歴を抽出する [PowerShell]

2011-09-20 17:19:19 | Winコマンド

PowerShellを使ってイベントログを照会してそこからWindows起動・ユーザーログオンについてのログを抽出しようという試み。
コマンドプロンプトでもできなくはないですが、PowerShellならオブジェクトとしてデータを利用できて便利なので今回はPowerShellを利用しようと思います。

○対象とする動作環境と必要事項

  • Windows Vista、7、Windows Server 2008 R2
  • .net Framework 3.5以降
  • Windows PowerShell 2.0以降
  • イベントログの"Security"ログにアクセスするには管理者権限が必要
  • 監査ポリシーの各種イベント監査が有効であること

○"Get-EventLog"コマンドでWindowsの起動履歴を抽出

"Get-EventLog"コマンドでイベントログを取得して"Where-Object"コマンドでフィルターをかける方法です。PowerShellではよく用いる一般的な手法ですが、イベントログの取得は後述の"Get-WinEvent"コマンドのほうが便利です。
なお、次のコマンドはPowerShellを管理者権限で開いた上で実行してください。"Security"ログへのアクセスには管理者権限が必要です。管理者権限でない場合はエラーが返ってきます。

$ get-eventlog Security -newest 8000 | where-object {($_.Source -eq "Microsoft-Windows-Security-Auditing") -and ($_.InstanceID -eq 4608)}
   Index Time          EntryType   Source                 InstanceID Message
   ----- ----          ---------   ------                 ---------- -------
   67191 9 21 08:40    SuccessA... Microsoft-Windows...         4608 Windows を起動しています。...
   65480 9 19 16:38    SuccessA... Microsoft-Windows...         4608 Windows を起動しています。...
   62326 9 14 12:09    SuccessA... Microsoft-Windows...         4608 Windows を起動しています。...
   62190 9 14 04:53    SuccessA... Microsoft-Windows...         4608 Windows を起動しています。...
   61589 9 12 18:33    SuccessA... Microsoft-Windows...         4608 Windows を起動しています。...

"Security"で"Security"ログを参照します。ログを全て取得しようとすると大変時間がかかるので"-newest 8000"スイッチで抽出ログを最新8000件以内に絞っています。そして"Where-Object"コマンドでソースが"Microsoft-Windows-Security-Auditing"かつイベントIDが"4608"のイベントのみのログを抽出します。特定の種類のログを絞り込むにはイベントIDだけでなくソースを指定する必要があります。
これら自体はWindowsの起動ログではなく"Lsass.exe"(ローカルセキュリティ機関)の起動ログですが、Windows起動時に必ず実行されてかつログに残るのでこれを利用しています。スタンバイや休止状態への移行・復帰は含まないため、普段シャットダウンさせないテレビ兼用パソコン等ではほとんどログが出てこないと思います。
こちらに"4608 - Windows is starting up."という説明がありました。

ログの結果から日時だけをファイルに出力するには次のようにします。

get-eventlog  Security -newest 8000 | where-object {($_.Source -eq "Microsoft-Windows-Security-Auditing") -and ($_.InstanceID -eq 4608)} `
| format-table -property TimeGenerated -autosize | out-file ($env:userprofile + "\Desktop\logBootup.log")

ここではデスクトップの"logBootup.log"ファイルに保存するように指定しています。保存先は適宜変えてください。
後述の"Get-WinEvent"コマンドを利用する場合は次のようにします。この方法ならより待ち時間を短くできます、

get-winevent -logname Security -filterxpath "*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and EventID='4608']]" `
| format-table -property TimeCreated -autosize | out-file ($env:userprofile + "\Desktop\logBootup.log")

"Get-EventLog"コマンドでは"TimeGenerated"プロパティ、"Get-WinEvent"コマンドでは"TimeCreated"プロパティにログの記録日時が格納されています。
この違いは返されるオブジェクトの型の違いによるものです。"Get-EventLog"コマンドでは"System.Diagnostics.EventLogEntry"クラス、"Get-WinEvent"コマンドでは"System.Diagnostics.Eventing.Reader.EventLogRecord"クラスのオブジェクト配列が返されます。

○"Get-WinEvent"コマンドで特定ユーザーのログオン履歴を抽出

"Get-WinEvent"は"Get-EventLog"をより強化したコマンドです。XMLベースのクエリを使ってフィルターをかけたり、各ログをXMLデータに変換することができます。このコマンドの実行にはWindows Vista、Windows Server 2008 R2以降かつ.NET Framework 3.5以降が必要です。

$ get-winevent -logname Security -filterxpath "*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and EventID='4624']] and *[EventData[Data[@Name='TargetUserName']='hogeUser']]" -maxevents 10
TimeCreated            ProviderName                        Id Message
-----------            ------------                        -- -------
2011/09/19 23:42:54    Microsoft-Windows-Security...     4624 アカウントが正常にログオン...
2011/09/19 20:17:31    Microsoft-Windows-Security...     4624 アカウントが正常にログオン...
2011/09/19 19:32:42    Microsoft-Windows-Security...     4624 アカウントが正常にログオン...
2011/09/18 22:07:02    Microsoft-Windows-Security...     4624 アカウントが正常にログオン...
2011/09/18 21:23:58    Microsoft-Windows-Security...     4624 アカウントが正常にログオン...
2011/09/18 19:17:13    Microsoft-Windows-Security...     4624 アカウントが正常にログオン...
2011/09/17 19:09:37    Microsoft-Windows-Security...     4624 アカウントが正常にログオン...
2011/09/16 23:07:56    Microsoft-Windows-Security...     4624 アカウントが正常にログオン...
2011/09/16 20:02:26    Microsoft-Windows-Security...     4624 アカウントが正常にログオン...
2011/09/15 21:57:54    Microsoft-Windows-Security...     4624 アカウントが正常にログオン...

PowerShellにおけるXMLの扱い方や"Get-WinEvent"コマンドについては日本語の解説サイトがほとんどなかったためいろいろ苦労しました。

イベントログ XMLデータ
上の画像はログオンについてのイベントログのXMLデータをイベントビューアで見たところ。
このようにイベントログはそれぞれXMLデータとして参照・利用することができます。また、XPathで記述したクエリを使ってイベントログにフィルターをかけることもできます。

それを踏まえたうえでPowerShellの説明に戻ると、"-filterxpath"スイッチにはXPathでクエリ(フィルター)を記述します。ここでは"="比較演算子を使ってノード集合を絞り込んでいます。具体的には"System/Provider"の"Name"属性が"Microsoft-Windows-Security-Auditing"に一致し、かつ"System/EventID"タグの内容が"4624"に一致、さらに"EventData/Data Name='TargetUserName'"タグの内容が"hogeUser"に一致するイベントログを取得しています。"hogeUser"には対象とするユーザー名を入れてください。
最後に付いている"-maxevents 10"スイッチは最大10件までのイベントを返すように指定しています。ここも適宜変えてください。

上と同じように"Format-Table"コマンドや"Out-File"コマンドとパイプで連結することで日時をファイルに出力できます。
出力されるログオン履歴にはネットワークログオンも含まれます。特に他のコンピューターとファイルを共有している場合は接続セッション維持のためのログオンの履歴が多数出てくると思います。
ここまでコマンドが1行に収まるようにしてきましたが、スクリプトで実行するなら変数を使って代入や実行処理を複数行に分けてもいいでしょう。

今回は"Get-WinEvent"コマンドとXPathを使ったイベントログフィルターの利用を紹介しました。各イベントログを"ToXML()"メソッドでXMLデータに変換すればさらにいろんなことに応用できるのですが、今回はここまでにしておきます。

○更新履歴

  • 2011/12/07 get-wineventコマンドでログ名の指定を忘れていたので修正
  • 2011/09/28 3つ目の例のformat-tableコマンドで取り出すプロパティ名が間違っていたのを修正
  • 2011/09/23 Vistaで起動ログが取得できなかった問題、その他を訂正
  • 2011/09/21 作成

コンソールアプリケーションとDOSアプリケーションを見分ける

2011-05-25 17:23:58 | Winコマンド

WindowsコンソールアプリケーションとMS-DOSアプリケーションを見分ける方法を紹介。

Windowsコンソールアプリケーション(以下コンソールアプリ)とMS-DOSアプリケーション(以下DOSアプリ)はWindows上はどちらもコマンドプロンプト上で動作するという点で同じだが、根本的には全く異なるもの。
しかしMS-DOSやWindows 9x時代のMS-DOSプロンプトからの流れでコマンドプロンプト上で実行するアプリをまとめてDOSコマンドなどと呼ぶ人が多く、
それが混乱を引き起こす要因になっている。

コンソールアプリとDOSアプリの動作の仕組みとその違いは以下のページを参考にするとよい(上級者向け)

ここではコンソールアプリとDOSアプリを見分けるためのそれぞれの特徴をいくつか紹介する。

・DOSアプリはプロパティ画面のタブの数が多い。
コンソールアプリ

DOSアプリ

実行ファイルを右クリック→プロパティ を開くとDOSアプリはタブの数が多いことがわかる。
これはDOSアプリにはMS-DOSエミュレーションの設定を行う項目があるためである。
Windowsが実行ファイルをDOSアプリだと認識すると、プロパティにMS-DOSエミュレーションに関する設定項目を表示する。

・DOSアプリは自動でショートカットが作成される。

DOSアプリを実行したりプロパティを開いたりすると同じフォルダーにショートカットが勝手に作られる。
これは先に述べたMS-DOSエミュレーションの設定を保存するため。

・コンソールアプリをメモ帳などのテキストエディターで開くと先頭付近に「This program cannot be run in DOS mode.」という文が見える。
コンソールアプリ

DOSアプリ

コンソールアプリは32ビットまたは64ビットのWindowsアプリケーションであり、16ビットOSであるMS-DOSでは動作しない。
WindowsアプリケーションはMS-DOSで実行すると「This program cannot be run in DOS mode.」というメッセージを表示して終了するようになっている。
このプログラムが先頭に埋め込まれているので、テキストエディターで開くとこのメッセージが見える。
一方DOSアプリにはそのようなメッセージは見当たらない。

・DOSアプリを実行すると英語環境に切り替わる (Windows Vista以降)
コンソールアプリ

DOSアプリ

Windows Vista以降ではMS-DOSの日本語環境(日本語モード)が削除されている。
その代わりにDOSアプリを実行しようとすると英語環境(海外仕様のMS-DOS)に切り替えて処理が行われる。
英語環境は日本語環境とフォントや画面モードが異なるため、ウィンドウの大きさやフォントが切り替わる。
またMS-DOSにはフォルダー・ファイル名は8文字(+拡張子3文字)までという制限があり、Windows上では名前が長いフォルダー・ファイルに対して一定の法則で「短い名前」が付けられている。
MS-DOSモード時はファイルアクセスに「短い名前」を利用するため、上の画像の例では「Documents」が「DOCUME~1」になっている。

ちなみに「chcp 932」を実行すると日本語環境のコマンドプロンプトに戻ることができる(Windows 7では自動で戻る。)

・DOSアプリは日本語のメッセージが文字化けする (Windows Vista以降)
コンソールアプリ(Windows Vista)

DOSアプリ(Windows XP)
LHA version 2.13
DOSアプリ(Windows Vista)

先に述べたようにWindows Vista以降でDOSアプリを実行すると強制的に英語環境に切り替わる。
そのため日本語環境で作成されたDOSアプリの全角文字のメッセージや罫線などは文字化けする。
場合によってはフリーズして強制終了させられる。

日本語環境での文字コードはコードページ932いわゆるShift_JISだが、このShift_JISの第1バイトにあたる部分は英語環境のコードページ437では西ヨーロッパ言語の文字が割り当てられている。
また、日本語環境のDOSと英語環境のDOSはソフトウェア的には互換性はあるものの画面表示の仕組みは全く異なるもので、
製品版MS-DOS/VではCHEVコマンドで日本語モードと英語モードを切り替えるようになっていた。
(つまり日本語環境のDOSと英語環境のDOSは、根は別物だということ。細かい事情はWikipedia「DOS/V」を参照。)
こういった事情があり、英語環境オンリーのDOSでは日本語を扱うことはできない。

ちなみに逆の例もあり、英語環境で作成されたDOSアプリ(海外のDOSアプリ)を日本語環境で実行すると罫線文字などが文字化けする。
この場合はCHEVコマンドやCHCPコマンドで英語環境に切り替えればいいのでさほど問題にはならない。
Vista以降では勝手に英語環境に切り替わるのでなおさら問題ない。




○関連
・XPモードで16ビットアプリケーションを利用する [Win7]
http://blog.goo.ne.jp/limited_terra/e/206e0039ad9152eee0040b6f87600a2b


バッチファイルでのドラッグ&ドロップ対応 [Win,コマンドプロンプト]

2011-04-21 18:30:21 | Winコマンド

ファイルのドラッグアンドドロップに対応したWindowsコマンドプロンプトのバッチファイルを作ろうという試み。
あくまでコマンドプロンプトはコマンド・スクリプトベースであり、それ自体がマウス操作を受け付けるわけではありません...
サンプルプログラムはバッチファイルを作ってそのまま実行するのではなく、何かファイルやフォルダーをバッチファイルにドラッグ&ドロップして実行してください。

○引数について

引数の解説→ http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/percent.mspx

基本的には引数と同じように扱えばいいのでやることはそれほど難しくありません。
例えば file1 file2 ...をバッチファイル上にドラッグ&ドロップすると実行パスは
「[バッチファイル] [file1の絶対パス] [file2の絶対パス] ...」
となります。
バッチファイル上では %1 %2 でfile1 file2 の絶対パスを取得できます。
ドラッグ&ドロップしたファイルやフォルダーの絶対パスにスペースが含まれている場合は勝手に引用句が付きます。
コマンドプロンプトの制限により、パスに;や=などの半角記号が含まれているとスペースに変換されるため正しく処理できません。

例1)
@echo off
echo すべての引数(%%*): %*
echo 実行コマンドパス(%%0): %0
echo 引数1(%%1): %1
echo 引数2(%%2): %2
pause
echo on


○修飾子を利用する

%1 %2 ...で取得したパスは引用句が付いていたりそうでなかったりするので修飾子を利用するといいでしょう。
これを利用すれば引用句を削除したり、相対パスを絶対パスにしたりできます。
またファイルの属性やサイズを取得することもできます。

以下forコマンドのヘルプより抜粋
%~I すべての引用句 (") を削除して、%I を展開します。
%~fI %I を完全修飾パス名に展開します。
%~dI %I をドライブ文字だけに展開します。
%~pI %I をパス名だけに展開します。
%~nI %I をファイル名だけに展開します。
%~xI %I をファイル拡張子だけに展開します。
%~sI 展開されたパスは短い名前だけを含みます。
%~aI %I をファイルの属性に展開します。
%~tI %I ファイルの日付/時刻に展開します。
%~zI %I ファイルのサイズに展開します。
%~$PATH:I PATH環境変数に指定されているディレクトリを検索し、最初に見つかった完全修飾名に %I を展開します。環境変数名が定義されていない場合、または検索してもファイルが見つからなかった場合は、この修飾子を指定すると空の文字列に展開されます。

例2)
@echo off
echo %%1:%1
echo %%~1:%~1
echo %%~f1:%~f1
echo %%~d1:%~d1
echo %%~p1:%~p1
echo %%~n1:%~n1
echo %%~x1:%~x1
echo %%~s1:%~s1
echo %%~a1:%~a1
echo %%~t1:%~t1
echo %%~z1:%~z1
echo %%~$PATH:1:%~$PATH:1
pause
echo on


○応用例1 : ファイルかフォルダーかを判別する

パスがファイルかフォルダーかを判別するには%~aIを使います。
上の修飾子の説明からすると%~nIや%~xIが使えそうな感じがしますが、修飾子はパスがファイルかフォルダーか、そもそも存在するパスなのか判断していませんので使えません。
"."を含むパスがファイルではなくフォルダーである可能性もあるからです。

この例ではいわゆるECHO-FIND-IFを使っています。
つまり %~a1 に d が含まれていたらフォルダーであると判断します。
例3)
@echo off
echo %~a1|find "d">NUL
if %ERRORLEVEL% EQU 0 (
echo %~1はフォルダーです。
) else (
echo %~1はファイルです。
)
pause
echo on


○応用例2 : 不特定多数の入力パスに対応する

バッチファイルでは %1~%9 を使って9個の引数を取得できます。
10個目以降の引数については %10 などは使えず、SHIFTコマンドを使って引数をずらす必要があります。
次は例2を複数ファイル・フォルダーのドロップに対応させたバージョンです。
例4)
@echo off
setlocal enableextensions
echo すべての引数:%*
:repeat
set /a count=%count%+1
echo 引数%count%
echo %%1:%1
echo %%~1:%~1
echo %%~f1:%~f1
echo %%~d1:%~d1
echo %%~p1:%~p1
echo %%~n1:%~n1
echo %%~x1:%~x1
echo %%~s1:%~s1
echo %%~a1:%~a1
echo %%~t1:%~t1
echo %%~z1:%~z1
echo %%~$PATH:1:%~$PATH:1
echo.
if "%~2"=="" goto end
shift
goto repeat
:end
endlocal
pause
echo on


○応用例3 : 総まとめ

ドラッグ&ドロップしたファイルをMAKECABコマンドを使ってMicrosoftキャビネット形式に圧縮するバッチです。
コマンドプロンプトから直接バッチファイルを実行することもできます。
ドラッグ&ドロップせずにバッチファイルを実行、もしくはパスを指定せずにバッチファイルを実行した場合は使用例を表示して終了します。
MAKECABコマンドの仕様ではフォルダーパスは受け付けないので、指定したパスがフォルダーであればエラーメッセージを表示します。
複雑になるのでしませんでしたが、もっと工夫すればフォルダー入力にも対応できるでしょう。

@echo off
setlocal enableextensions
if "%~1"=="" goto show_help
:repeat
echo 圧縮中: "%~f1"
if not exist "%~f1" goto error_nofile
echo %~a1|find "d">NUL
if %ERRORLEVEL% EQU 0 goto error_isfolder
makecab "%~f1">NUL
goto next
:error_nofile
echo 指定したファイル"%~f1"が見つかりません。
goto next
:error_isfolder
echo フォルダーの圧縮はできません。
goto next
:next
if "%~2"=="" goto end
shift
goto repeat
:show_help
echo 使用例:cp path1 [path2 [...]]
echo  または
echo  エクスプローラー上でファイル(複数可)をバッチファイルにドラッグ^&ドロップする。
:end
endlocal
echo on

ifコマンドを活用する [Win,コマンドプロンプト]

2011-02-01 01:00:21 | Winコマンド

コマンドプロンプトで主にバッチファイルで使われる ifコマンドについてまとめてみる。

参考サイトhttp://technet.microsoft.com/ja-jp/library/cc754335%28WS.10%29.aspx

○3つの基本構文

IF [NOT] ERRORLEVEL 番号 コマンド
IF [NOT] 文字列1==文字列2 コマンド
IF [NOT] EXIST ファイル名 コマンド

これら3つの基本形はMS-DOS初期のバージョンからWindows 7のコマンドプロンプトまで20年以上に渡ってサポートしているので、互換性の面ではまず心配ないだろう。

○基本例1

sample_program.exe
if errorlevel 1 echo プログラムが異常終了しました。
ver|find "Windows XP" > NUL
if not errorlevel 1 echo ご使用中のOSはWindows XPです。

多くのプログラムは終了時に終了コードというのを返す。ほとんどのプログラムは正常終了時に0、異常終了時に1以上の値を返すが、必ずしもそうと決まっているわけではない。
2行目はsample_program.exeが1以上の終了コードを返したらメッセージを表示する。
3,4行目はやや応用的で、findコマンドは指定した文字列を見つけると0を返した上でその文字が含まれる行を表示する。指定した文字列が見つからないと1を返す。つまり、verコマンドの実行結果に「Windows XP」が含まれていたら終了コード0を返す。ここでif errorlevel 0~ としてしまうと終了コードが0以上の時にコマンドを実行するので、意図したとおりに動作しない。if not errorlevel 1 つまり終了コードが1以上でない = 0以下のとき、とすることで正しく動作する

コマンドプロンプトでは代わりにERRORLEVEL環境変数を用いることで後述の基本例3のように比較演算子を使って条件分岐が可能。MS-DOSやWin9xのMS-DOSプロンプトではERRORLEVEL変数をサポートしていない。

○基本例2

set abc=test
if %abc%==test echo 変数abcと"test"は一致しています。
if "%def%"=="test test" echo 変数defと"test test"は一致しています。

「abc」という変数に「test」という文字列を入れ、2行目で変数abcの内容と「test」を比較している。
変数がスペースを含む、もしくは含む可能性がある場合は3行目のようにする。
引用符は必須ではないが、比較対象の変数が空だったりすると構文エラーになることがあるので常に引用符を付けておいたほうがいい。
引用符も文字列比較の対象に入るので、両方の比較対象に引用符を付けないと一致したことにならない。

○基本例3

if exist C:¥file.txt echo ファイル C:¥file.txtは 存在します。
if exist C:¥dir¥NUL echo ディレクトリ C:¥dir は存在します。

コマンドプロンプトではファイルだけでなくフォルダパスも指定できるが、MS-DOSやMS-DOSプロンプトではサポートしていない。
ただし、例の2行目のようにファイル名としてNULを指定することでフォルダーの有無を確認できる。

○コマンド拡張機能有効時に使える構文

IF [/I] 文字列1 比較演算子 文字列2 コマンド
IF CMDEXTVERSION 番号 コマンド
IF DEFINED 変数 コマンド

これらはコマンドプロンプトでのみ使用可能で、MS-DOSやMS-DOSプロンプトではサポートしていない。
コマンド拡張機能とはWindows NT系に搭載されているコマンドプロンプト(cmd.exe)で新たに拡張された機能で、具体的には、バッチファイルで使うIF、FOR、SETコマンドなどの追加機能のこと。便利な機能が使えるようになった反面、新しい機能によってこれまでのMS-DOS/Win9x用バッチファイルが正しく動作しないことがある。そのため、コマンド拡張機能は標準では有効になっているが、レジストリの設定やSETLOCALコマンドなどによってこれを無効にできる。

○基本例4

set num1=30
set num2=20
if %num1% GTR %num2% (echo %num1%>%num2%
) else if %num1% EQU %num2% (echo %num1%=%num2%
) else if %num1% LSS %num2% echo %num1%<%num2%

例はnum1とnum2の数字の大小を調べている。
比較演算子はWindows 2000以降では小文字でもよいが、NT4.0以前では大文字でないとコマンドを受け付けない。
括弧については後ほど補足。

○基本例5

if not cmdextversion 2 echo このバージョンのWindowsではバッチファイルを正しく実行できない可能性があります。

あまり使う機会がない構文。使い方はerrorlevelと同じ。
ちなみにコマンド拡張機能のバージョンはWindows NT4.0までが1、Windows 2000、XP、Vista、7は2である。

○基本例6

if defined windir echo Windowsフォルダは%windir%にあります。

これもそれほど使う機会はない。
コマンド拡張機能を使わずに環境変数が定義されているかどうかを確認するには、

if not "%var%"=="" echo 環境変数 var は定義されています。

とする。

○応用例1 : 剰余を計算する(数値比較、複数行の条件分岐)

set num1=24
set num2=10
if %num2% EQU 0 (
echo 0除算エラーです。
goto :eof
) else if %num2% LSS 0 (
echo 負の値は指定できません。
goto :eof
)
set /a ans=%num1%%%%num2%
if errorlevel 1 (
echo 演算処理に失敗しました。
) else if %ans% EQU 0 (
echo 余りはありません。
) else (
echo %num1%÷%num2%の余りは%ans%です。
)

例はnum1をnum2で割った余りを求めている。
括弧でくくることで条件分岐の中で複数のコマンドを実行することができるが、この形はコマンドプロンプトでのみ使用可能。
if文の中にif文を含める 入れ子構造 も処理してくれる。
elseは独立したコマンドではなくifコマンドの構文に含まれるため、if文と同じ行に置く必要がある。
この例ではelseの前に終わり括弧を置くことで、if文がこの行まで続くことを示している。

1つ注意点があって、先に述べたようにプロンプトはif文の括弧の始まりから終わりまでを1つの文として一括で読み取り解釈する。
つまりif文の途中で変数の値を変えて、さらにそのif文中でその変数を使った処理をしようとすると、意図したとおりにならないことがある。
この問題は「遅延環境変数の展開」機能を利用することで解決できる。

○応用例1+ : 遅延環境変数の展開を使った応用例1の簡略版

setlocal enabledelayedexpansion
set num1=24
set num2=10
if %num2% EQU 0 (echo 0除算エラーです。
) else if %num2% LSS 0 (echo 負の値は指定できません。
) else (
set /a ans=%num1%%%%num2%
if errorlevel 1 (echo 演算処理に失敗しました。
) else if !ans! EQU 0 (echo 余りはありません。
) else echo %num1%÷%num2%の余りは!ans!です。
)
endlocal

試しにこの例の!ans!を%ans%に戻して実行してみると、「0 の使い方が誤っています。」というエラーが表示される。
これはプロンプトが4~11行目を解釈した時点では変数ansに値が設定されておらず、
9行目で「if  EQU 0 ~」という誤った構文のコマンドを実行したためである。
変数num1、num2については、if文に入る前の2、3行目で設定しているためエラーにはならない。

○応用例2 : 指定したフォルダ内のフォルダ数を取得 (入力メソッド、数値比較)

:retry
echo フォルダパスを入力してEnterキーを押してください。
echo 何も入力せずにEnterキーを押すとこのバッチファイルを終了します。
set fddir=
set /p fddir=^>
if not defined fddir goto :eof
if not exist %fddir% goto retry
>folderlist.txt dir /ad /b %fddir%
for /F "delims=" %%a in ('find /v /c "" folderlist.txt') do set fdcount=%%a
if %fdcount:~27% EQU 0 (echo %fddir%内にサブフォルダはありません。) else echo %fddir%内にサブフォルダは%fdcount:~27%個あります。
del folderlist.txt

6行目は入力メソッドで何も入力しなかったときに終了。
7行目は不正なパスが入力された時に再度入力要求。
10行目で数字の比較。
ちなみに4行目は一見意味がないように見えるが、1度目で不正なパスを入力した後、2度目以降の入力メソッドで終了できるために必要になる。


Windowsのバージョン毎に処理を分岐させる [コマンドプロンプト]

2010-12-05 00:33:16 | Winコマンド

Windowsのバージョン毎に処理を分けるバッチファイルを作ってみました。

○VERコマンド実行結果まとめ

Windows NT Version 4.0

Microsoft Windows 2000 [Version 5.00.2195]

(XP 32ビット版)
Microsoft Windows XP [Version 5.1.2600]

(XP Professional x64 Edition & Server 2003)
Microsoft Windows [Version 5.2.3790]

(Vista Service Pack 1 & Server 2008)
Microsoft Windows [Version 6.0.6001]

(7 & Server 2008 R2)
Microsoft Windows [Version 6.1.7600]

(8 & Server 2012)
Microsoft Windows [Version 6.2.9200]

○バージョン別VERコマンド実行イメージ
IBM DOS J5.0/V
MS-DOS 6.2/V
Windows 98 Second Edition
Windows 2000 Service Pack 4
Windows XP 32-bit
Windows Vista Service Pack 2
Windows 7 Service Pack 1

○コマンドプロンプト

参考元(http://malektips.com/xp_dos_0025.html)を参考に少し改良を加えています。
Windows Vista以降のVERコマンドではOS名がフルで出てこないので、SYSTEMINFOコマンドを実行してその結果の中からOS名を抜き出しています。
コマンド拡張機能を利用しているためDOS(MS-DOS,PC DOS)やMS-DOSプロンプト(Win9X,Me)では動きません。
DOS対応版は旧版を参照してください。32ビット/64ビット判別やService Packの判別はこちらを参照して下さい。

rem check windows version---------------
setlocal enableextensions
ver|find "NT">NUL
if %errorlevel% EQU 0 goto winnt
ver|find "2000">NUL
if %errorlevel% EQU 0 goto win2k
ver|find "XP">NUL
if %errorlevel% EQU 0 goto winxp
if not exist %SystemRoot%\System32\systeminfo.exe goto warn
for /F "tokens=2 delims=," %%a in ('%SystemRoot%\System32\systeminfo.exe /FO CSV /NH') do set osvers=%%~a
echo %osvers%|find "Windows ServerR 2003">NUL
if %errorlevel% EQU 0 goto win2k3
echo %osvers%|find "Windows Vista">NUL
if %errorlevel% EQU 0 goto winvista
echo %osvers%|find "Windows ServerR 2008">NUL
if %errorlevel% EQU 0 goto win2k8
echo %osvers%|find "Windows 7">NUL
if %errorlevel% EQU 0 goto win7
echo %osvers%|find "Windows Server 2012">NUL
if %errorlevel% EQU 0 goto win2012
goto warn

:winnt
echo Windows NT
goto :EOF
:win2k
echo Windows 2000
goto :eof
:winxp
echo Windows XP
goto :eof
:win2k3
echo Windows Server 2003
goto :eof
:winvista
echo Windows Vista
goto :eof
:win2k8
echo Windows Server 2008
goto :eof
:win7
echo Windows 7
goto :eof
:win2012
echo Windows Server 2012
goto :eof
:warn
echo Unknown Windows version

VERコマンドを実行してその出力をパイプでFINDコマンドに入力。
FINDコマンドの終了コードが0、つまり指定した文字列が見つかればそのOSの処理に飛びます。
FORコマンドではCSV形式で出力したSYSTEMINFOコマンドの実行結果を解析し、指定した部分を変数osversに代入しています。
その後ECHOコマンドで変数osversの値を出力し、それをパイプでFINDコマンドに...としています。
条件分岐後はECHOコマンドでOS名を表示していますが、実際はここに処理するコマンドを記述していくことになります。


以下補足説明。

○MS-DOSにおけるVERコマンドの仕様

機能 : DOSのバージョン番号を表示する。内部コマンド。
対応OS : MS-DOS Version 2.0以降
書式 : VER [/R]
スイッチ : /R - リビジョン管理情報とDOSのハイメモリ領域での動作状況を表示する[MS-DOS 5.0以降]
終了コード : なし
補足 : バージョン番号の小数点以下の文字だけが変わるようなマイナーバージョンアップ(リビジョンアップ)はVERコマンドには反映されていないことがある。
例えばIBMのDOS 5.02はDOS 5.00、NECのMS-DOS 3.3AはMS-DOS 3.30など。

※Windowsでの変更点

書式 : VER
スイッチ : なし
終了コード : 0 - 正常終了、1 - 異常終了(コマンドの書式が誤っている。)
補足 : Windows XPまではバージョン番号とともにOS名もきちんと表示されていたが、
Windows XP Professional x64 EditionとWindows Server 2003 x64版で同じカーネルを用いたのをきっかけにOS名の表示は"Microsoft Windows"だけになる。
代わりにXPで追加されたSYSTEMINFOコマンドを用いて完全なOS名を取得できる。

○SYSTEMINFO

機能 : 基本システム構成情報を参照する。外部コマンド。
対応OS : Microsoft Windows XP以降
書式 : SYSTEMINFO [/S システム [/U ユーザー名 [/P パスワード]]] [/FO 形式] [/NH]
スイッチ : /S システム - 接続先のリモートシステム(コンピューター名やIPアドレスなど)を指定する。
/U [ドメイン\]ユーザー - 実行するコマンドのユーザーを指定する。
/P [パスワード] - ユーザーのパスワードを指定する。パスワードを省略した場合は入力を要求する。
/FO 形式 - 出力形式を("TABLE","LIST","CSV")で指定する。このスイッチを省略するとLIST形式で出力される。
/NH - 項目名を省略して値のみを出力する。
終了コード : 0 - 正常終了、1 - 異常終了(コマンドの書式が誤っている、リモートシステムに接続できない、など。)
補足 : 対応OSはWindows XP以降だがWindows XP Home Editionでは利用できない。
/S、/U、/Pスイッチはネットワーク上のコンピューターの情報を取得する際に利用する。

○FIND

機能 : テキストファイル(複数可)から指定された文字列を検索し、それを含む行を表示する。外部コマンド。
対応OS : MS-DOS Version 2.0以降
書式 : FIND [/V] [/C] [/N] [/I] [/S] "文字列" [[ドライブ:][パス]ファイル名[...]]
スイッチ : /V - 指定した文字列を含まない行を表示する。
/C - 指定した文字列を含む行の数だけを表示する。
/N - 行番号を表示する。
/I - 大文字と小文字の区別をしないで検索する[MS-DOS 5.0以降]
/S - サブディレクトリも検索する[PC DOS 7.0]
"文字列" - 検索する文字列を指定する。
[ドライブ:][パス]ファイル名 - 検索するファイル(複数可) を指定する。
終了コード : 0 - 指定した文字列が1箇所以上見つかった。
1 - 検索は正常に終了したが指定した文字列が見つからなかった。
2 - 異常終了(コマンドの書式が誤っている、検索対象のファイルが存在しない、など。)
補足 : 引用符「"」を検索文字列に指定するには「""」とする必要がある。
検索対象ファイルを指定する代わりにパイプを利用することで、コマンドの出力結果を処理することができる。
検索対象を指定しなかった場合は標準入力(キーボード)から入力される文字列を処理する。
PC DOS 7.0のみ/Sスイッチと検索ファイル指定にワイルドカード("*"と"?")を利用できる。
コードページが932の場合、テキストファイルの文字コードがシフトJIS以外だと正しく処理できない。

※Windowsでの変更点

書式 : FIND [/V] [/C] [/N] [/I] [/OFF[LINE]] "文字列" [[ドライブ:][パス]ファイル名[...]]
スイッチ : /OFF[LINE] - オフライン属性が設定されたファイルをスキップしない。
補足 : 検索ファイルの指定にワイルドカード("*"と"?")を利用できるようになった。
MS-DOSではワイルドカードをサポートしていないが、FORコマンドと組み合わせることで同様の処理ができる。
/OFFスイッチはオフラインファイルを検索対象に含める場合に付ける。

○更新履歴

  • 12/10/30 Windows Server 2012に対応
  • 12/06/07 Windows Server 2003,2008で正しく処理されない問題を修正
  • 11/05/23 一部削除とコマンドの補足
  • 11/05/08 一部削除と修正
  • 10/12/05 いろいろ修正
  • 10/10/25 作成
コメント (2)

Windowsのバージョン毎に処理を分岐 [コマンドプロンプト](旧版)

2010-12-05 00:33:15 | Winコマンド

Windowsのバージョン毎に処理を分けるバッチファイルを作ってみました。

○verコマンド実行結果まとめ

(DOS/V)
PC DOS Version J6.10/V

(IBM互換機、PC-98)
MS-DOS Version 6.20

(95 OEM Service Release 2)
Windows 95. [Version 4.00.1111]

Windows NT Version 4.0

(98 Second Edition)
Windows 98 [Version 4.10.2222]

Microsoft Windows 2000 [Version 5.00.2195]

Windows Millenium [Version 4.90.3000]

(XP 32ビット版)
Microsoft Windows XP [Version 5.1.2600]

(XP Professional x64 Edition & Server 2003)
Microsoft Windows [Version 5.2.3790]

(Vista Service Pack 1 & Server 2008)
Microsoft Windows [Version 6.0.6001]

(7 & Server 2008 R2)
Microsoft Windows [Version 6.1.7600]



○MS-DOSプロンプト&コマンドプロンプト
参考元(http://malektips.com/xp_dos_0025.html)を参考に少し改良を加えています。
一応MS-DOSでも反応するようになっていますがDOS互換OSやOS/2には対応していません。
Windows Vista以降のverコマンドではOS名がフルで出てこないので、systeminfoコマンドを実行してその結果の中からOS名を抜き出しています。
同じようにsysteminfoコマンドから"システムの種類"を抜き出せば32ビット/64ビットの区別もできるでしょう。

rem check windows version---------------
ver|find "DOS">NUL
if not errorlevel 1 goto dos
ver|find "Windows 95">NUL
if not errorlevel 1 goto win95
ver|find "Windows 98">NUL
if not errorlevel 1 goto win98
ver|find "Windows Millennium">NUL
if not errorlevel 1 goto winme
ver|find "NT">NUL
if not errorlevel 1 goto winnt
ver|find "2000">NUL
if not errorlevel 1 goto win2k
ver|find "XP">NUL
if not errorlevel 1 goto winxp
ver|find "2003">NUL
if not errorlevel 1 goto win2k3
if not exist %SystemRoot%\system32\systeminfo.exe goto warn
setlocal enableextensions
for /f "tokens=2 delims=," %%a in ('%SystemRoot%\system32\systeminfo.exe /FO CSV /NH') do set osvers=%%a
echo %osvers%|find "Windows Vista">NUL
if not errorlevel 1 goto winvista
echo %osvers%|find "Windows ServerR 2008">NUL
if not errorlevel 1 goto win2k8
echo %osvers%|find "Windows 7">NUL
if not errorlevel 1 goto win7
goto warn

:dos
echo DOS
goto eof
:win95
echo Windows 95
goto eof
:win98
echo Windows 98
goto eof
:winme
echo Windows Me
goto eof
:winnt
echo Windows NT
goto eof
:win2k
echo Windows 2000
goto eof
:winxp
echo Windows XP
goto eof
:win2k3
echo Windows 2003
goto eof
:winvista
echo Windows Vista
goto eof
:win2k8
echo Windows 2008
goto eof
:win7
echo Windows 7
goto eof
:warn
echo Unknown Windows version
:eof

verコマンドを実行してその出力結果をパイプでfindコマンドに入力。
findコマンドの終了コードが0 つまり指定した文字列が見つかればそのOSの処理に飛びます。
for~ではCSV形式で出力したsysteminfo.exeの実行結果を解析し、指定した部分を変数osversに代入しています。
Windows Vista以降で動くことを前提にするならもっと単純で分かり易いスクリプトが作れたかと思います。

○PowerShell
PowerShellは使い慣れていないのでこのサンプルは参考程度に。
ここではバージョン取得に.net FrameworkのSystem.Environmentクラス、32ビットか64ビットかを見分けるためにWMIを利用しています。

$osver_major = [Environment]::osversion.Version.Major
$osver_minor = [Environment]::osversion.Version.Minor
$systype = (Get-WmiObject -Class Win32_ComputerSystem -ComputerName .).systemtype
if ($osver_major -lt 7) {
    switch ($osver_major){
        "5"{ switch ($osver_minor){
                "1" {write-host "Windows XP (i386)"}
                "2" {write-host "Windows XP Professional x64 Edition"}
                default {write-host "?"}
            } }
        "6"{ switch ($osver_minor){
                "1" {write-host "Windows 7" -nonewline}
                "0" {write-host "Windows Vista" -nonewline}
                default {write-host "?"}
            }
            switch ($systype) {
                "X86-based PC" {write-host " (32-bit)"}
                "X64-based PC" {write-host " (64-bit)"}
                default {write-host "(?)"}
                }
            }
       default {write-host "?"}
       }
} else {
    write-host "?"
}

64ビット版Windowsが手元にないのでちゃんと振り分けられるかどうか不明。
Windows Serverとの区別をしていないので若干問題あり。
.net Framework 4ではIs64BitOperatingSystemプロパティが追加されていますが、互換性を考えて使いませんでした。
また、WMIだけでもバージョン取得から32/64ビットの区別まで済ませることができますが、ここではあえて両方使いました。
この他にもWindowsのバージョンを取得・分岐する方法はまだまだたくさんあるでしょう。

10/12/05 いろいろ修正
10/10/25 作成


MS-DOS,Windows OS別対応コマンド一覧

2010-11-28 16:53:04 | Winコマンド

MS-DOS 3.3, 5.0, Windows 98のcommand.com、2000, XP, Vista, 7のcmd.exeが対応している内部および外部コマンドの一部をまとめてみた(あくまでコマンド一覧なので、各コマンドの解説は他のサイトを参照。)

HELPコマンドで表示されるコマンドを中心にまとめたので、基本的なコマンドはすべてこの一覧にあると思うが、Windows 95, 98はHELPコマンドがなく資料も少ないので、いくつか不十分なところがあるかも。
Windows Vistaと7ではHELPで出力されるコマンドは同じ。
注1: 表のOSはすべてPC/AT互換機版で、IBM DOSや各PCメーカー固有の追加コマンドは含んでいない。
注2: MS-IMEなどのFEP関連のコマンドは抜いたつもり。
注3: 同じコマンドを持っていても仕様が異なることがある。
(例:dirコマンドでサポートするスイッチは、Win98では/4 /A /B /L /O /P /S /V /W
WinXPでは/4 /A /B /C /D /L /N /O /P /Q /S /W /X)

○=実行可 ×=実行不可もしくは存在しても本来の機能を持っていない
APPEND ASKYESNO ASSIGN ASSOC AT ATTRIB BACKUP BCDEDIT BREAK CACLS CALL CD(CHDIR) CHCP CHEV CHKDSK CHKNTFS CHOICE CLS CMD CMDKEY COLOR COMMAND COMP COMPACT CONVERT COPY CSCRIPT CTTY CVT DATE DEBUG DEL(ERASE) DELTREE DIR DISKCOMP DISKCOPY DISKPART DOSKEY DOSREP DOSSHELL DRIVERQUERY DUMP ECHO EDIT EDLIN EMM386 ENDLOCAL EXE2BIN EXIT EXPAND EXTRACT FASTOPEN FC FDISK FIND FINDSTR FOR FORMAT FSUTIL FTYPE GOTO GPRESULT GRAFTABL GRAPHICS
GWBASIC HELP ICACLS IEXTRACT IF JOIN JVIEW KEYB LABEL LFNFOR LH(LOADHIGH) LINK LOADFIX LOCK MD(MKDIR) MEM MIRROR MKLINK MODE MORE MOVE MSCDEX NLSFUNC OPENFILES PATH PAUSE POPD PRINT PROMPT PUSHD QBASIC RD(RMDIR) RECOVER REM REN(RENAME) REPLACE RESTORE ROBOCOPY SC SCANDISK SCANREG SCHTASKS SELECT SELKKC SET SETLOCAL SETUP SETVER SHARE SHIFT SHUTDOWN SMARTDRV SORT START SUBST SYS SYSTEMINFO TASKKILL TASKLIST TIME TITLE TREE TRUENAME TYPE UNDELETE UNFORMAT UNLOCK USRFNT VER VERIFY VOL WIN WMIC XCOPY

Windows Vista以降ではDOSアプリ(16ビットアプリケーション)を実行すると強制的に英語環境になる。
英語環境では日本語のメッセージが文字化けするので日本語のDOSアプリは実用できない。
XPモードで16ビットアプリケーションを利用する [Win7]

Windows VistaではDOSアプリ終了後も英語環境のまま。Windows 7ではDOSアプリ終了後に日本語環境に戻る。しかしどちらにせよ、日本語のDOSアプリは文字化けして使い物にならない。
当然のことだがBIOSの割り込みルーチンを利用するDOSアプリ、FM TOWNS・PC-98などの独自アーキテクチャーに依存するDOSアプリはまともに動かない。

下の表はARPやTRACERT(TRACEROUTE)といったネットワーク診断の基本的なツールと上級者向けの外部コマンドである。
上級者向けコマンドについてはほとんどがGUI上で行える操作なので、一般ユーザーはよほど何らかの事情がない限りはお世話になることはないだろう。
ここにあるコマンドは、ただ単純にSYSTEM32フォルダからコマンドプロンプト上で実行するものをVistaを中心として表にしただけなので、環境によっては存在しないコマンドもある。

-? や /? スイッチを指定して実行するとGUI上でヘルプが表示されるものは省略している。
また、Vistaで削除されたコマンドや7で追加されたコマンドは省略。下の詳細データファイルには省略せずに入れておいたのでそちらを参照。

下書き&詳細データファイル:shosai.xlsx(MS Excel 2007形式)


○更新
10/11/28 WinXP Homeを追加
10/06/10 一覧をいろいろ修正
10/06/06 作成


echoコマンドのいろんな使い方 [Win,コマンドプロンプト]

2010-08-10 20:29:54 | Winコマンド

コマンドプロンプトで主にバッチファイルで使われる echo コマンドについてまとめてみる。

参考サイトhttp://technet.microsoft.com/ja-jp/library/cc772462%28WS.10%29.aspx
MS公式サイトであるだけに説明がよくまとまっているのに、なぜか検索エンジンの上位に引っかかってこない不思議。

○基本1
echo [on | off]
エコーをオン(既定)、オフにする。
エコーがオンとはユーザーが自分でコマンドプロンプトを開いた時の状態。
つまり、カーソルが出てその左側に C:~> と表示される状態(プロンプトを表示するともいう。)
エコーがオンのままバッチファイルを実行すると、バッチファイル内の実行コマンドも表示される。
バッチファイルを実行する時はエコーをオフにしておくのが基本。
デバッグの際はエコーをオンにしておけば、もしエラーが起きて実行が中止されてもどのコマンドで止まったのかがわかる。

○基本例1

@echo off
~ここにコマンドを書く~
echo on

デスクトップやエクスプローラからバッチファイルを実行した場合、最後の行のコマンドの実行が終わるとコマンドプロンプトは閉じるが、
あらかじめコマンドプロンプトを開いた状態でバッチファイルを実行した場合は、最後に「exit」を実行しないとバッチファイルの実行が終わっても待機状態で止まる。
なので終了させずにバッチファイルを終える場合は最後に「echo on」を入れておいた方がいいだろう。
先頭に「@」を付けるとその行だけを"echo off"する、つまりechoがonになっていてもその行だけ実行コマンドを表示しなくなる。
「@」はechoだけに限らず様々なコマンドに付けることができるので、これも可。

@%SYSTEMROOT%\system32\msinfo32.exe
@>samp.txt dir



○基本2
echo [メッセージ]
メッセージ(文字列)を出力する(行末に改行を含む。)

○基本例2

echo ABCDEFG HIJKLMN
echo.
echo.on

1行目は基本的な使い方。
2,3行目はちょっと変わった使い方で、2行目はただの改行を表し、3行目はonを表示する。
echoのすぐ次の文字に記号(.,;=+など)を付けるとその文字だけ無視される。
つまり、1行目を「echo=ABCDEFG HIJKLMN」としても同じ出力を得られる。
記号がスペース扱いのようになるのはechoコマンドの直後の文字だけなので、
「echo,+=ABCDEFG.HIJKLMN」は「+=ABCDEFG.HIJKLMN」と出力される。

「>、|、<」をリテラルとして出力するにはそれぞれ前に「^」を付ける必要がある。
また「^」自身を出力するには「^^」とする必要がある。

○応用例1

echo a|xcopy *.txt d:

xcopyコマンドでコピー先に同名のファイルがあった時に上書きをするかの質問に対して、a(上書きする、以降のファイルも同様)を自動で入力してくれる。
あまり一般的な使い方ではないし実用にも向いてないと思う。


○応用例2

echo カキコてすと。>samp.txt
echo 2行目てすと>>samp.txt

リダイレクトを使うことでメッセージ内容をファイルに書き出す裏技。

ただしこの形だと欠点があって、例えばバッチファイルでiniファイル(設定ファイル)を作ろうとして、
「echo example_key=8>samp.ini」などとすると、標準出力に「example_key=」と表示されてsamp.iniには何も書かれない。
これは "8>" の部分を入出力ストリームのリダイレクトとして判断されてしまうせいだと思われる。
ちなみに「echo example_key=274>samp.ini」は数字が3桁のためか274の部分は入出力ストリームのリダイレクトとは判断されず、よってこれはsamp.iniに正しく出力される。

これを解決するためには「>samp.ini echo example_key=8」とすればよい。
複数行にわたって書き出す時は>を>>にするだけ。こうしたほうが見易さもよくなるのでおすすめ。

最後にいろんなテクニックを組み合わせてみた。

@echo off
rem バージョンチェック(三項演算子らしき構文)
(ver|find "Windows 2000">NUL&&echo 注意:このバージョンのWindowsでは正しく動作しないことがあります。)||echo WindowsバージョンチェックOK
echo 処理中 ^& しばらくお待ちください...
>samp.ini echo 設定項目1=1
>>samp.ini echo 設定項目2=2
>>samp.ini echo 設定項目3=3
>>samp.ini echo.on
rem 1行で複数行出力
>>samp.ini echo 設定項目4=4&>>samp.ini echo.&>>samp.ini echo 設定項目5=5
rem 複数行で1行出力
>>samp.ini echo 設定パス6^
=%%PATH6%%
echo 設定ファイルを作成しました。
rem 制御コードを使ってBEEP!!
echo.
echo ^>^>^> Press Any Key ^<^<^<&pause >NUL
echo on