(for Internet Explorer)
← ▼ ▲
↑
Line (TextStream)
Property Get TextStream::Line() as integer
テキストストリームの現在の行番号。（１〜）
次にアクセスする行番号。
(Web)
１行目をリード/ライトしたら、Line = 2 になります。
ファイルの最後の行をリードしたときは、＋１されません。
← ▼ ▲
AtEndOfStream (TextStream)
Dim TextStream::AtEndOfStream as boolean
ストリームの最後なら True
(Web)
Shell:Exec で起動したプログラムから出力される標準出力ストリームに
バッファリングされた文字がないときは、False を返します。
標準入力ストリームにバッファリングされた文字がないときは、待ち状態に
入ります。改行文字がバッファリングされ、改行文字の前に文字が入力されて
いなかったら True、文字が入力されていたら False を返します。
ファイルの最後が改行文字のとき、その改行文字をリードしたら、AtEndOfStream
= True になります。
end (Return)
f.AtEndOfStream = False
ReadLine = "end"
f.AtEndOfStream = True
ファイル
メソッドの結果
リードした内容が入っている変数が無効かどうかではなく、最後の行かどうかです。
変数 = Null にしておけば、空行である "" と一致しません。
変数 = Empty にしておけば、空行である "" と一致します。
← ▼ ▲
Write (TextStream)
Sub TextStream::Write( Text as string )
テキストストリームに出力します。
(Web)
← ▼ ▲
WriteLine (TextStream)
Sub TextStream::WriteLine( Text as string )
テキストストリームにテキストと改行を出力します。
← ▼ ▲
WriteBlankLines (TextStream)
(Web)
Sub TextStream::WriteBlankLines( LengthOfLine as integer )
テキストストリームに複数の改行を出力します。
← ▼ ▲
Shell::Exec で実行すると、pause ができない
'********************************************************************************
' <<< [EchoStream] echo supported No vbCRLF >>>
'********************************************************************************
Dim g_EchoStreamBuf
Sub EchoStream( StreamIn, StreamOut )
Dim s, c
Do Until StreamIn.AtEndOfStream
c = StreamIn.Read(1)
If g_EchoStreamBuf="続行するには何かキーを押してください . . ." Then
Stop
End If
If c <> vbCR and c <> vbLF Then _
g_EchoStreamBuf = g_EchoStreamBuf + c
If c = vbLF Then
WScript.Echo vbCR + g_EchoStreamBuf : g_EchoStreamBuf = ""
Else
StreamOut.Write c
End If
Loop
End Sub
Shell::Exec
で起動したコマンドライン・プログラムでは、バッチファイルの pause が正しく
動きません。
WshScriptExec::StdIn.ReadLine を呼ぶと、改行コードが入力されるまで戻ってきません。
WshScriptExec::StdIn.AtEndOfStream や、Read、ReadAll もなぜか戻ってきません。
このため、pause で表示される「続きを実行するとき…」のメッセージは表示されません。
１文字ずつ表示しても、文字の終了が AtEndOfStream で判定しようとすると戻ってこなく
なるので、使えません。
１文字ずつ表示する echo だが、AtEndOfStream で止まることの実証コード：
2&>1 で標準エラー出力を標準出力にリダイレクトすると、止まらないことがあります。
← ▼ ▲
ファイルコピーの引数
CopyFile
CopyFolder
MoveFile
MoveFolder
CopyFile、MoveFile
dst 引数
src 引数
ファイル a.txt
コピー先フォルダ C:\dst_folder が存在するとき
動作
C:\dst_folder
エラー 800A0046 書き込みできません
C:\dst_folder\a.txt
dst_folder に a.txt をコピーします
C:\dst_folder\b.txt
ファイル名を b.txt に変えてコピーします
CopyFolder、MoveFolder
エラー 800A004C パスが見つかりません
C:\dst_folder\bb\cc
フォルダ aa を dst_folder の中にコピーします
C:\dst_folder\aa
フォルダ aa の内容を dst_folder にコピーします
C:\dst_folder
動作
フォルダ aa
src 引数
dst 引数
コピー先フォルダ C:\dst_folder が存在するとき
に指定するパスがどうなっているかどうかで、
どのような動きをするかをまとめています。
C:\dst_folder\c\d.txt
エラー 800A004C パスが見つかりません
フォルダ名を bb に変えてコピーします
C:\dst_folder\bb
ワイルドカードは、コピー先にマッチしないときに限り使えます。
フォルダ名を変えることはできません。
ワイルドカード
C:\dst_folder
dst_folder にファイルをコピーします
フォルダはコピーしません
C:\dst_folder\c
エラー 800A004C パスが見つかりません
ワイルドカード
C:\dst_folder
フォルダを dst_folder の中にコピーします
copy
なお、vbslib の
ありません。
は、指定したパスがどうなっているかどうかによって動きが変わることは
src にワイルドカードがあるときは、dst はフォルダーとして処理します。
dst がフォルダーとして存在するときは、そのフォルダーの中にコピーします。
そうでないときは、dst がファイルとして処理します。
← ▼ ▲
Set fs = CreateObject("Scripting.FileSystemObject")
fs.CopyFolder "public", "dst\public", True
Set fs = Nothing
フォルダをコピーする
新しくできるフォルダ名
または、上書きするフォルダ名まで含める
WinCE = "C:\WINCE420"
If MsgBox( "coredllをインストールします。"+Chr(13)+WinCE+"\platform\SG2_VR5500\drivers\ipLib\ipLibCom\ipLib-com.h" _
+Chr(13)+WinCE+"\private\WINCEOS\COREOS\CORE\DLL"+Chr(13)+_
WinCE+"\public\common\cesysgen\Makefile"+Chr(13)+_
"を上書きし、"+Chr(13)+_
WinCE+"\PLATFORM\SG2_VR5500\INC\ipLibCom.h などを作成します。"+Chr(13)+_
"よろしいですか？ （ドライブは合っていますか？）"+Chr(13)+_
"（リビルドするときや他の環境で使うときは、アンインストールしてください。）" ,_
vbYesNo + vbExclamation, "上書き確認" ) = vbYes Then
Set fs = CreateObject("Scripting.FileSystemObject")
fs.CopyFolder "platform", WinCE+"\platform", TRUE
fs.CopyFolder "private", WinCE+"\private", TRUE
fs.CopyFolder "public", WinCE+"\public", TRUE
Set fs = Nothing
Set sh = WScript.CreateObject("WScript.Shell")
sh.Run WinCE+"\PLATFORM\SG2_VR5500\DRIVERS\ipLib\ipLib-Headers-maker.vbs",1,True
Set sh = Nothing
MsgBox "インストールが完了しました。"
End If
Sub main()
Set args = WScript.Arguments
If args.Count <> 2 Then MsgBox "This is update files copy script" _
+vbCR+"ex) WScript.exe xcopy_pb.vbs c:\folder1 c:\folder2", vbOKOnly, "xcopy_pb" : Exit Sub
Set fs = CreateObject( "Scripting.FileSystemObject" )
Set folder1 = fs.GetFolder( args(0) )
Set folder2 = fs.GetFolder( args(1) )
For Each file In folder1.Files
fname1 = args(0) + "\" + file.Name
fname2 = args(1) + "\" + file.Name
Set file1 = fs.GetFile( fname1 )
On Error Resume Next
Set file2 = fs.GetFile( fname2 )
On Error Goto 0
If IsEmpty( file2 ) Then
fs.CopyFile fname1, fname2
ElseIf file1.DateLastModified > file2.DateLastModified Then
fs.CopyFile fname1, fname2
End If
Next
End Sub
main
新しいファイルのみコピーする
（サブフォルダはコピーしません）
← ▼ ▲
ファイル一覧
Dim g_fs, fo, f
Set g_fs = WScript.CreateObject( "Scripting.FileSystemObject" )
Set fo = g_fs.GetFolder( "c:\folder" )
For Each f in fo.Files
f.Name, f.Path ' ここに処理を書く
Next
fo = Empty
g_fs = Empty
Dim g_fs, fo, subfo
Set g_fs = WScript.CreateObject( "Scripting.FileSystemObject" )
Set fo = fs.GetFolder( "c:\folder" )
For Each subfo in fo.SubFolders
subfo.Name, subfo.Path ' ここに処理を書く
Next
fo = Empty
g_fs = Empty
サブフォルダ一覧 （１段下のみ）
Set g_fs = CreateObject("Scripting.FileSystemObject")
EnumSubFolders g_fs.GetFolder( "C:\folder" )
Sub EnumSubFolders( fo )
For Each f in fo.Files
f.Name, f.Path ' ここに処理を書く
Next
For Each subfo in fo.SubFolders
EnumSubFolders subfo
Next
End Sub
サブフォルダ一覧 （再帰、ネスト）
サンプル：
SubFolders.vbs.lzh
サブフォルダのすべてのファイルにコマンドを実行し、
標準出力をファイルに出力します。
ファイルの存在
フォルダの中にあるファイルの一覧です。
フォルダの中のフォルダについては、
→ サブフォルダ一覧 （１段下のみ）
フォルダの中にあるフォルダの一覧です。
フォルダの中のファイルについては、
→ ファイル一覧
ファイル一覧、存在
→ FileSystemObject::FileExists
→ FileSystemObject::FolderExists
存在しないファイルを開く
Dim i
For i = 1 To 9999
If i<9999 Then On Error Resume Next 'try
If i=1 Then : Test_LogFName = "test_log.txt"
Else : Test_LogFName = "test_log" & i & ".txt" : End If
Set g_log = g_fs.CreateTextFile( Test_LogFName, True, False )
e.Copy( Err ) : On Error GoTo 0 : If e.num <> 0 Then 'catch
If e.num <> 70 Then e.Raise
Else
Exit For
End If
Next
→ Folder オブジェクト
→ Folder オブジェクト
→ ファイル一覧
関連
← ▼ ▲
ファイル内アクセス（テキスト）
Set g_fs = WScript.CreateObject( "Scripting.FileSystemObject" )
Set file = g_fs.CreateTextFile( path ******, True, False )
file.Write "改行なし。"
file.WriteLine "ファイル= " & path '最後に 0x0D, 0x0A を出します
file.WriteBlankLines 1 'ブランク行を1行書き込む
file.Close ' 下記 Empty でもクローズするので不要
file = Empty
g_fs = Empty
テキストファイルを作成する (Write)
Set g_fs = WScript.CreateObject( "Scripting.FileSystemObject" )
Set f = g_fs.OpenTextFile( path ******,,,-2 )
Do Until f.AtEndOfStream
line = f.ReadLine() ' 改行(vbCR+vbLF)は含まない
Loop
f = Empty
g_fs = Empty
テキストファイルの内容を読む (Read)
↑上書き、非Unicode
Set g_fs = CreateObject( "Scripting.FileSystemObject" )
Set f = g_fs.OpenTextFile( read_path1 ****** )
t = f.ReadAll
f = Empty ' 無くてもよい（ガベージコレクタでクローズされるため）
Set f = g_fs.OpenTextFile( read_path2 ****** )
t = t + f.ReadAll
f = Empty
Set f = g_fs.CreateTextFile( write_path ******, True )
f.Write t
f = Empty
テキストファイルを結合する
→ テキストファイルを作成する (Write)
→ テキストファイルの内容を読む (Read)
base64.zip (VBS)
バイナリファイルは扱えないようです。
CreateTextFile の返り値は、TextStreamオブジェクトです。
ADOのStreamを使えばできるらしい
→ CreateTextFile メソッド (Web)
→ テキストファイルを結合する
→ FileSystemObject::OpenTextFile
Dim g_fs, f
Set g_fs = CreateObject( "Scripting.FileSystemObject" )
Const ForAppending = 8
Set f = g_fs.OpenTextFile( path ******, ForAppending, True, False )
f.WriteLine "A"
テキストファイルに追記する (Appending Write)
→ テキストファイルに追記する (Appending Write)
ReadLine
OpenTextFile
OpenTextFile
CreateTextFile
CreateTextFile
Set g_fs = WScript.CreateObject( "Scripting.FileSystemObject" )
Set f = g_fs.OpenTextFile( path ****** )
t = ""
On Error Resume Next
t = f.ReadAll()
en = Err.Number : ed = Err.Description : On Error GoTo 0
If en = E_EndOfFile Then en = 0
If en <> 0 Then Err.Raise en,,ed
f = Empty
ReadAll を使う場合
テキストファイルの一部を置き換える
→ テキストファイルの一部を置き換える
→ TextStream
Set g_fs = CreateObject( "Scripting.FileSystemObject" )
Dim rf, wf, line, src_path, dst_path
src_path = folder_path + "\sample.c"
dst_path = folder_path + "\sample.c_"
If Not g_fs.FileExists( dst_path ) Then
mkdir g_fs.GetParentFolderName( dst_path )
Set rf = g_fs.OpenTextFile( src_path )
Set wf = g_fs.CreateTextFile( dst_path, True )
Do Until rf.AtEndOfStream
line = rf.ReadLine()
line = Replace( line, "from", "to" )
wf.WriteLine line
Loop
rf = Empty : wf = Empty
copy dst_path, src_path
del dst_path
End If
folder_path + "\sample.c"
folder_path + "\sample.c_"
to
from
Replace
CreateTextFile
OpenTextFile
→ ReadAll
関連
→ OpenForRead (vbslib)
→ OpenForWrite (vbslib)
関連
→ ADODB.Stream
← ▼ ▲
DeleteFolderR
'***********************************************************************
' <<< [DeleteFolderR] リードオンリーファイルを含むフォルダでも削除する >>>
'************************************************************************/
Sub DeleteFolderR( path )
Set fs = CreateObject( "Scripting.FileSystemObject" )
If fs.FolderExists( path ) Then Exit Sub
path2 = path ' 末尾の \ 記号を取る
If Right( path2, 1 ) = "\" Then path2 = Left( path2, Len( path2 ) - 1 )
nFolder = 1
ReDim folderPathes(nFolder)
folderPathes(nFolder) = path2
' サブフォルダを列挙する
iFolder = 1
While iFolder <= nFolder
Set fo = fs.GetFolder( folderPathes(iFolder) )
For Each sub in fo.SubFolders
nFolder = nFolder + 1
ReDim folderPathes(nFolder)
folderPathes(nFolder) = sub.Path
Next
Set fo = Nothing
iFolder = iFolder + 1
WEnd
' すべてのサブフォルダにあるファイルのリードオンリー属性を外す
For iFolder = 1 To nFolder
Set fo = fs.GetFolder( folderPathes(iFolder) )
For Each f in fo.Files
Set file = fs.GetFile( f.Path )
file.Attributes = file.Attributes And Not ReadOnly
Set file = Nothing
Next
Set fo = Nothing
Next
' フォルダを削除する
fs.DeleteFolder( path )
Set fs = Nothing
End Sub
CreateFolder
→ FileSystemObject::CreateFolder
← ▼ ▲
変数、Empty、型、配列
Option Explicit
Dim v1, v2
Set v1 = CreateObject(...)
:
fs = Empty
Option Explicit は、変数宣言が必要である
ようにします。
Set する前に Dim で宣言する必要があります。
If IsEmpty( var ) Then
Dim var して、初期化していないときは、Empty
→ Dim の詳細
Set a = Nothing
If a is Nothing Then
Empty は、変数に何も値が入っていないことを示します。 未初期化と同じです。
Null は、変数に何も値が入っていないことを示します。 初期化済みとは区別できます。
Null と比較すると、演算結果は常に Null になります。
Nothing は、何も無いことを示すオブジェクトです。
返り値がオブジェクトのときは、呼び出し側が Set を使うので、
Nothing を返したほうがいいでしょう。
→ 配列、Redim Preserve
Empty などの比較は、次のようになります。
Empty = "" '// True, 真
not Empty '// -1, 真
Null = "" '// Null, 偽
Null <> "" '// Null, 偽
If IsObject( b ) Then Set a = b Else a = b
下記のようにすれば、オブジェクトでもそうでなくても代入できます。
a = Null
If IsNull( a ) Then
Is 演算子
２つの変数が参照しているオブジェクトが同じ１つのオブジェクトかどうかを
比較します。 メンバ変数の値が全く同じでもオブジェクトが異なるときは、
False となります。 is 演算子の意味は、クラスの is-a 関係とは異なります。
C言語における、ポインタの値の比較に相当します。
Empty, Nothing, Null
Set 命令
→ Set 命令
オブジェクトを代入します
オブジェクトを代入します
→ Empty, Nothing
「無い」値
→ Is 演算子
→ 型の判定
同一オブジェクトの判定
変数に入っている値の型
→ 単体と配列の共用変数
Nothing はオブジェクトなので Set が必要
Nothing かどうかは、is で判定
Null は値なので Set が不要
If objs(i) is obj Then ...
← ▼ ▲
Dim の詳細
Dim は、グローバルで実行するとグローバルスコープに、
プロシージャで実行するとローカルスコープになります。
グローバルスコープとローカルスコープの両方で宣言すると、ローカルスコープの変数だけ
見えますが、グローバルスコープの変数も別に存在します。
プロシージャの中で Execute してインクルードしたコードは、ローカルスコープになります。
Dim した後で、同じ変数名を Dim したときは、すでに使えるので無視され、
エラーにもなりません。
Dim は実行フェーズで評価されます。 文法解析時は評価しません。
Dim していない変数であるというエラーは、変数を評価するときに発生します。
→ Dim ステートメント (Web)
← ▼ ▲
Option Explicit 変数宣言不要論
変数の宣言が必要なプログラミング言語の場合、変数の宣言をし忘れて書き直すことが
よくあり、コストになっています。 さらに、任意型の宣言は、意味がよく分からないため、
可読性を下げます。 逆に言えば、変数宣言が不要であれば、可読性を上げるのです。
このためか、新しいプログラミング言語 Python などは、ローカル変数の宣言を無くしています。
Option Explicit の無い VBScript では、
・変数宣言が不要になります
・グローバル変数に定義されていない名前を、関数の中で使った場合、期待通り、
ローカル変数になります
・グローバル変数に定義してある名前を、関数の中で使った場合、ローカル変数のつもり
で使ったものは、実はグローバル変数なので、期待した動作になりません。（参考：下記、
変数名が衝突する問題）
次の点は、Option Explicit があってもなくても変わりません。
・関数から呼び出し元に戻ったら、ローカル変数は削除されます。
・引数は、ローカル変数になります。
・ 呼び出し元のローカル変数の名前と衝突しても、期待通り、別のメモリの変数になります。
（ちなみに、bash の場合は、同じメモリになる）
変数名が衝突する問題
変数宣言を不要とした場合、変数名が衝突していないことを裏付けることが、Dim
文を実行するときから、テストに合格するときまで遅れます。 これにより、衝突して
いる問題を解決することが、少しだけ難しくなります。
→ Option Explicit 変数宣言について (vbslib)
参考
→ 変数宣言を強制しないといけないのか。 (Web)
参考
(cache)
→ T_NoOptionExplicit フォルダ
テスト
エラー・メッセージの違い
<ERROR err_number='500' err_description='この変数は宣言されていません。'/>
Option Explicit があるとき
<ERROR err_number='13' err_description='型が一致しません。'/>
Option Explicit がないとき
関数として定義されていないシンボルを使って関数呼び出しをしようとすると、
Option Explicit があるときと、無いときで、エラーメッセージが異なります。
VBScript の Dim は、変数名の衝突を早期に検出すること以外に機能はありません。
プログラミング言語によっては変数の型を決めることがありますが、VBScript は、
変数にどの型の値でも入るため変数の型を決める機能はありません。
衝突を検出するだけの命令文は、プログラムが何かをする命令文ではありません。
これは問題です。 プログラムが何をしようとしているのかを理解しようとするときに、
実は何もしていないモノに遭遇しても、何をしようとしているのか懸命に探します。
この脳の状態で、実は何もしていない、ということに気づくのは大変なことです。
大きなコストになります。 また、何もしていないことを読まされることは不快です。
Dim 命令の説明でよくあるのが、使っている変数を挙げているとありますが、別に
Dim 命令が無くても使っている変数は分かります。 変数を初期化している文で、
よく理解できます。 Dim はくどいのです。
なぜ、型が一致しませんと表示されるかというと、定義されていない関数と同じ
名前の新しい変数が自動的に生成され、初期値である Empty が入り、Empty
を使って関数呼び出しをしようと WSH が解釈してしまうからです。
下記のホームページでは、Dim の必要性を説明していますが、必要の根拠となる機能
が無くても、すぐに別のエラーで知ることが出来るため、問題として挙げられていること
が真の問題の解決を困難にすることはありません。
VBScript で、Option Explicit を使った場合、よく遭遇するのは、変数を宣言していない
というエラーです。 このエラーを対処するには、デバッガーを使う形で最初からやり直して、
衝突している変数を知って、Dim 宣言を追加して、また最初からやり直す必要があります。
つまり、２回も再起動が必要になります。 これは結構なコストです。 もし、デバッガー
が無ければ、対処は更に困難になります。
Option Explicit を使っている人は、このコストの大きさに気づいていません。
（私も気づくのに時間がかかりました）
Option Explicit があるときとないときの違い
その他の問題
ローカル変数同士が衝突した場合、原因は、その関数の中だけに絞られます。
つまり、呼び出した先の関数に原因がある一般的なミスに比べると、原因がある範囲が
限定的なので、対処が比較的簡単です。
グローバル変数同士の衝突は、一般的なミスの対処と変わりません。 また、Option
Explicit を使っても、VBS ファイルをモジュール別に分けていると、Dim を使っても衝突を
検出できません。
ローカル変数のつもりで使った変数が、実はグローバル変数だったとき、つまり、
グローバル変数がローカル変数に衝突した場合は、ローカル変数のつもりでデバッグを
行おうとするため、対処が困難でしょう。 しかし、グローバル変数の名前の先頭に "g_"
を付けるコーディング・ルールなどを適用すれば、その問題は無くなります。 Python
では、「global」 命令によって問題を回避しています。
Sub MyTest()
name = "MyName"
name = "YourName"
Assert FuncA( name ) = "Taro"
End Sub
どれだけ難しくなるかを、見てみましょう。
これは、name = "MyName" であることを期待しているテスト・スクリプトで、name が
"YourName" に変わったためにテストに失敗しています。 この原因を追究するときは、
name の値をどこで代入しているかを調べることになります。 name を上方向に検索して、
name = "YourName" が見つかり、それを対処することになります。
実は、これは、変数が衝突したときよりも発生頻度が高い、一般的なミスによって name
の値が違っているケースと同じ対処法です。 確かに Dim 文によって衝突を検出した
方が速く原因が特定できますが、検出しなくても一般的な対処法で済むわけです。
変数名が衝突するミスは、Dim 文の記述を忘れるミスに比べると、かなり発生頻度が
低いです。 衝突する頻度が低い理由は、意味が異なる変数には、通常異なる名前を
付けるからです。 Dim 文の記述を忘れることが多い理由かは、Dim が何かをする
命令文ではないからです。 何かをさせるために記述しているときに、させることが全て
記述できたら、完成と思うからです。
以上から、発生頻度と修正コストを総合して考えると、Option Explicit をつけた方が
コストが高くなるというわけです。
←
↓