MS_VBS - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki

VBS

概要

トピック情報を纏めています。

  • 言語構文やプログラミングの解説は、ここでは割愛します。
  • 下記のガイドや、「vbs | vbscript やりたいこと」でググるなどしてください。
    豊富なサンプル、Q&A が、ネット上に多数転がっています。
  • ネット上に転がっていたスクリプトを引用する際は、権利関係に注意ください。
    (特に、まるまる引用するような場合)

補足(最新化:非推奨化が進行中): VBScript は
2023 年 10 月に Microsoft が非推奨(deprecated)を公表した。

段階 状況
第 1 段階 Windows 11 24H2 以降、**オンデマンド機能(FoD)**として提供(既定で有効)
第 2 段階 既定で無効
第 3 段階 完全に削除

新規に VBScript を書く理由はもう無い。
既存資産は PowerShell への移行を計画すること
スクリプティングに移行の勘所を記載)。

本ページの内容は、既存資産を読み解く/保守するための
参照情報として有用である。

VBScript と VBAの機能差

  • VBScript は、Excel VBA に似ています。

    • Excel のモジュールシートに記述する感じで記述できます。
    • ただ、構文や機能に、できることの違いが少しあります。
  • リファレンスの「VBScript ユーザーズ ガイド」から、

    • 「VBScript の機能で VBA に含まれていない機能」
    • 「VBA の機能で VBScript に含まれていない機能」

    を参照してください。

vbs と wsf

VBScript ファイルは、vbswsf の 2 種類あり、
どちらも WSH のスクリプトを記述します。

拡張子 内容
.vbs VBScript のみを記述。
.wsf XML ファイルで、VBScript のほか、JScript / 参照する TypeLib / 参照する(取り込む)script ファイルも記述できる。

VBScriptの勘所

以下、個別に。

set と let

VBScript で値を代入するときは、let 文を使います。
ただし、代入するものが object の場合は、set 文を使います。

let str1 = "abcde"
str2 = "ABCDE"
set fso = CreateObject("Scripting.FileSystemObject")

普通は、let は省略して、上の str2 の代入の様に書きます。
関数の戻り値の場合も同じで、戻り値を返す関数では、関数名へ set を使って代入します。

filename = "c:\temp\tempfile.txt"
set fso = CreateObject("Scripting.FileSystemObject")
set tmp1 = makeTempFile(filename)
tmp1.writeline "てすと"
tmp1.close
set tmp1 = nothing

function makeTempFile(fn)
  dim f
  set f = fso.OpenTextFile(fn, 2, True)  ' 2=書き込み, True=作成
  set makeTempFile = f
end function

sub と function

上の例の様に、sub ... end sub は、値を返さない関数を定義します。
値を返す場合は、function を使います。

function amari(a, b)
  amari = ( a mod b )
end function
print amari( 20, 3 )

Function の戻り値

  • 関数名と同じ変数へ代入します。
  • 関数の戻り値の型は指定できません。
    (vb や vba にある function funcname(arg1, arg2) as typeas type
    記述できません)

a mod b は、ab で割った剰余を求める演算子です。

Function の呼び出しは、

  • 値を必要とする場所(代入の右辺、別の関数の引数、式の途中)などに、
    関数名(引数,..) の様に記述して呼び出します。
  • また、値を必要としない場所では、関数名 引数,.. の様に ( ) をつけずに記述します。
  • 値を必要としない場所に ( ) をつけて記述する場合は、
    call 関数名(引数,..) の様にして戻り値を無視するようにします。

Sub の呼び出しは、

call 関数名(引数,..) / 関数名(引数,..) / 関数名 引数,.. どの形式でも使用できます。

補足(引数の括弧の罠): VBScript で最も分かりにくい仕様がここである。
引数が 1 つのとき、Foo (x) と書くと
(x) が「式の評価」と解釈され、値渡しになる

Sub Increment(ByRef n)
    n = n + 1
End Sub

Dim v : v = 1
Increment v      ' → v は 2 になる
Increment (v)    ' → v は 1 のまま(値渡しになる)

ByRef のつもりが効かない、という不具合の典型的な原因である。

変数の定義

Dim と スコープ

  • VBScript では、dim 文で変数を定義します。
  • スコープ
    • 関数の外で定義すると、グローバル変数になります。
    • class の中で定義することで、class のローカル変数にすることもできます。
    • sub または function の中で定義した変数は、その関数の中がスコープになります。

Option Explicit

  • VBScript は、dim 文を省略することもできますが、
    変数のはじめに現れた場所に依存して、スコープが関数内またはグローバルに決定されます。
  • 変数名の誤字によるバグを回避したり、変数のスコープを管理するためにも、
    VBScript ファイルの最初に Option Explicit を記述して、
    未定義の変数をエラーとして検出するようにしましょう。

Private

dim 文のほかに、定数を定義する const や、class を作成した際に
class 外部から参照させないための private もあります。

変数の型

なお、VBScript では、変数の型がありません。

dim var1 as string   ' ← エラーになる

VBScript では、

  • 全てが variant 型で、代入した値や変数によって、型が変化します。
  • また、数値型や文字列型とは自動で変換されます。
  • object 型とその他を明確に区別したい場合は、その型の値を代入します。

Empty と Null と Nothing

区別

isempty(変数名)
isnull(変数名)
isobject(変数名)
変数名 is nothing
意味
Empty 変数の初期状態。何か代入するとその値の型になる(型名は TypeName 関数で取得)
Null 「値が無い」ことを表す値。代入もできる
Nothing オブジェクト参照が無い状態。isobjectTrue を返す
dim str1
if isempty(str1) then print "str1 is empty"

str1 = 123
if NOT isempty(str1) then print "str1 is not empty. is " & typename(str1)

set obj1 = nothing
if isobject(obj1) then print "is object"
if obj1 is nothing then print "is nothing"

値とオブジェクト

  • 関数を定義する際、戻り値がある場合は、戻り値の型が値かオブジェクトかで、
    呼び出し元での代入文が変わりますので、どちらを返すのか決めて設計しましょう。
  • 関数への引数では、値とオブジェクトのどちらでも受け入れる場合は、
    isobject の真偽を確認して場合わけをします。
function add(dims, e)
  Redim Preserve dims( ubound(dims) +1 )
  if isobject(e) then
    set dims( ubound(dims) ) = e
  else
    dims( ubound(dims) ) = e
  end if
end function

dim arr
arr = array()     ' 要素0個の配列
add arr, "123"    ' 文字列を追加
add arr, fso      ' オブジェクトを追加

数値と数字

  • 数値と数字は、使用する場所によって、数値だったり文字列だったりします。
  • できるだけ、意識して使い分けましょう。
関数 内容
cint() 16 bit 整数に変換
clng() 32 bit 整数に変換
int() int に切り捨て
str1 = "456"

int1  = cint(str1)
long1 = clng(str1)
int2  = int(int1/10)     ' 45(45.6 を切捨て)
int3  = cint(int1/10)    ' 46(45.6 を四捨五入)
int4  = str1 + 123       ' 579
str4  = str1 + "123"     ' "456123"

補足(+ は使わない): 上の例が示す通り、
+オペランドの型によって加算にも連結にもなる
文字列連結には必ず & を使うこと。
また cint() は 16 bit(-32768〜32767)なので、
32768 以上でオーバーフローする点に注意。

文字と文字列

  • 文字も文字列も " " で囲んで記述します。" 自身は "" として記述します。
  • & は文字列を連結する演算子です。数値や数字は文字列に変換して連結します。
  • mid は文字列の一部を返します。mid(文字列, 開始位置, 長さ)
  • VBScript 内部では、文字列は Unicode で表現されているので、半角 / 全角の区別はありません。
  • VBScript には、文字列と文字の区別はありません。長さ 1 の文字列で代用します。
str1 = "123456"
str2 = mid(str1,3,1)
str3 = """123""と""456"""

str2 = chr(9)
str3 = "これは\0で区切っています" & chr(0) & "続きもある"

移行メモ(正誤): 元ページは mid の「開始位置は 0 が 1 文字目」
と記載していたが、VBScript の Mid は 1 始まりである
Mid("123456", 3, 1)"3" を返す)。
後述の instr も 1 始まりで、両者は整合している。

If文

Then

  • if 文は、then の右に処理を 1 つ書くか、then の下の行に処理を複数行書きます。
    • then の右に書いたときは、end if は省略できます。
    • then の下に書いたときは、elseifelse で続けて、end if で閉じます。
str4 = "これはどんな文字列かな"
if mid(str4,3,1) = "ど" then print "「ど」があるね"
if instr(str4,"文字") >= len(str4)/2 then
   print "後半に ""文字"" がある"
end if

式の評価順

VBScript の if 文は、式を全て評価してからif 文全体の真偽を判定します。

set obj = func1()
if (NOT obj is nothing) and obj.flag = 2 then
  ' foo
end if

この例の場合、obj がオブジェクトであっても、なくても .flag を参照するので、
obj がオブジェクトでなかった場合に、エラーになります。
下の様に、if 文をネストさせて、式の評価順に依存しないようにします。

if NOT obj is nothing then
  if obj.flag = 2 then
    ' foo
  end if
end if

補足: これは「短絡評価(ショートサーキット)が無い」という意味である。
C# の && / ||PowerShell-and / -or
短絡評価されるため、この書き分けは不要になる。
VBScript 固有の注意点として押さえておきたい。

継続行 と コメント

VBScript では、「 _」で次の行へ継続(_ の前にスペースあり)、
'」があると行末まで無視します。

str1 = "あいうえお"
str2 = mid (str1, _
            instr(str1, "う") -1 )  ' "う" 以後の文字列を取り出す

instr は、文字列を含む位置を返します(1〜、含まないなら 0)。

ファイル操作

ファイル操作は、Scripting.FileSystemObject を使用します。

const ForReading = 1
set fso = CreateObject("Scripting.FileSystemObject")
set ts = fso.OpenTextFile("c:\temp\tempfile.txt", ForReading)
print ts.readall
ts.close
set ts = nothing
set fso = nothing
  • Scripting.FileSystemObject によるファイル操作は、テキストファイルのみ動作します。
  • 文字コードは Windows 既定、Unicode を指定できますが、
    UTF-8 の様な文字コードは扱えません
  • バイナリデータも、chr(10) の様に文字コードを指定した文字列として扱うことは
    可能ですが、ファイル入出力時に、改行コードや BOM 文字、EOF 文字が処理されて、
    期待するバイナリファイルにならないことがあります。

補足(UTF-8 とバイナリの扱い): この制限は VBScript の実務上の
最大の壁である。回避策としては次が使われてきた。

用途 手段
UTF-8 の読み書き ADODB.StreamCharset = "UTF-8"
バイナリの読み書き ADODB.StreamType = adTypeBinary

ただし ADODB.Stream は BOM の扱いに癖がある。
PowerShell なら Get-Content -Encoding utf8 で済む話であり、
これも移行の動機になる。

エラー処理

  • エラーは、自分で値チェックなどで検出する論理エラーのほか、
    WSH その他が発生させるランタイムエラーがあります。

  • ランタイムエラーが発生すると、WSH そのものがエラー終了します。

  • COM などのオブジェクト呼び出しの先でエラーが発生した場合も、ランタイムエラーになります。

  • ランタイムエラーを VBScript 内で処理するには、on error を使います。

    • on error goto 0(VBScript をエラー終了する)
    • on error resume next(無視して続行、Err 変数にエラー番号が設定される)
err1 = 0                ' エラー番号
on error resume next    ' ランタイムエラーでVBScriptを終了させない
Err.Clear               ' Err変数をクリア
set obj1 = CreateObject("hogehoge.hogehoge1")
if Err.Number <> 0 then err1 = Err.Number   ' ランタイムエラー判定
if err1 = 0 then
  if obj1.hagehage("てすと") <> 0 then
    err1 = 1            ' 論理エラー
  end if
end if
if err1 <> 0 then
  print "エラー発生 " & err1
end if
wscript.quit err1       ' エラー番号 を VBScriptの終了コードに

補足(On Error Resume Next の危険): これは
以降すべてのエラーを黙って無視する」指定であり、
必要な範囲の直後で On Error GoTo 0 に戻すのが鉄則である。
スクリプト冒頭に書いたまま放置すると、
想定外のエラーも握り潰され、誤った結果のまま正常終了する。

debug方法

VBScript のデバッグは、

  • debug 用 print 文を埋め込み
  • Visual Studio などの開発ツール
  • Debugger — cscript.exe で実行するときに //x をオプションに指定すると、
    debugger が起動して、ステップ実行、変数の確認などができます。

wscript.echo と wscript.stdout.writeline

メソッド 出力先
wscript.echo 標準出力(cscript 時)または画面(wscript 時)
wscript.stdout.writeline 標準出力のみ。cscript.exe でのみ動作wscript.exe ではエラー)

画面の場合は、wscript.echo 1 つにつきメッセージボックスが 1 つ表示され、
[OK] を押すのを待ちます。

スクリプトで作り込む際には、下の様に、print 関数を定義する、などするといいでしょう。

sub print(v)
  wscript.stdout.writeline v
end sub

参考

リファレンス

@IT


Tags: 移行, シェル, インフラストラクチャ, Windows, Visual Basic, 移行

⚠️ **GitHub.com Fallback** ⚠️