Tech-Ezo (Hokkaido PC and Network Users Group)
 Top Page  |  What's Tech-Ezo  |  Next Seminar  |  Seminar Log  |  Seminar Plan  |  Tips  |  life  |  Link  |
Scr.003 ステップアップ WSH - 使えそうなスクリプトを作って見よう
前ページへ   [ステップアップ WSHへ戻る]   次ページへ


■  Level.10a [おまけ]  検索フォルダ/検索文字をコマンドライン引数から指定する
■ ■

 前回(Level.10)やLevel.8で宣言した通り、このスクリプトをバッチ処理に組み込むことを想定して、検索対象フォルダや検索文字の入力を、ダイヤログボックスからではなく、コマンドライン引数を使って指示するようにします。

 このレベルは勉強会では無かったもので、この後のレベルとも関係が有りませんので、「おまけ」扱いです。

コマンドライン引数を取得するには

 コマンドライン引数を取得するには、WScriptオブジェクトのArgumentsプロパティを使用します。まぁ、使い方はサンプルのコードを見れば直ぐ理解できると思いますので、まずは下記のスクリプトを作成して実行して見て下さい。

コマンドライン引数を表示する(arg.vbs)
001
002
003
004
Set arg = WScript.Arguments
For i=0 to arg.Count - 1
   WScript.Echo arg(i)
Next

 さっそく実行して見ましょう。

arg.vbsファイルをコマンドラインから実行
C:\>cd c:\script
C:\script>cscript arg.vbs 1 2 3 4
1
2
3
4

C:\script>cscript arg.vbs -d "c:\script\tips" -s "ファイル"
-d
"c:\script\tips"
-s
ファイル

C:\script>

 サンプルを見て頂ければお分かりかと思いますが、WScript.Argumentsをargという名前の変数として定義すると、引数を配列として参照できるようになります。arg(0), arg(1) ... などと参照するわけです。それと、いくつ引数に指定されたかは、arg.Countプロパティを参照することで判明します。

 最初の実行例では、引数に"1 2 3 4"を指定しています。結果は、各引数が1つずつ表示されるため、"1"、"2"、"3"、"4"と1行ずつ表示されています。

 2つ目の実行例では、実際のコマンドライン引数の指定方法を想定して入力して見ました。結果は見ての通りです、それぞれ空白で区切られた各フィールドがarg(i)に格納されているのが分かるかと思います。

引数チェック

 上記のようにコマンドライン引数を取得すること自体はとても簡単なのですが、実際にスクリプトの中で使うためには、引数に指定された文字列が期待する通りに入力されているかをチェックする必要があります。これがちょっと面倒なのです(ワタシ的にですけどね)。

 では、まず引数の使い方を決めましょう。

コマンドラインオプション
■形式
  cscript level10a.vbs -f <folder_name> -s <string>

■オプション
  -f <folder_name> ... フォルダ名の指定
  -s <string>      ... 検索文字列の指定

 さて、上記のようにコマンドラインの使い方を決めました。加えて、そうですね...こんなルールも追加してみましょう。

  • 各オプションは省略してもよい
  • オプションを省略した場合は従来通りダイヤログボックスを表示し入力を促す
  • 間違ったオプションや使い方をした場合にはエラーメッセージを表示し終了する

 まず、先程のarg.vbsを使ってこれらのルールを実現してみます。

引数チェック(arg.vbs)
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
' --< エラーメッセージ表示関数 >-------------------------------------------------
Sub err_sub()
    WScript.Echo "使い方"
    WScript.Echo "cscript level10a.vbs -f  -s "
    WScript.Echo "-f  ... 検索フォルダを指定"
    WScript.Echo "-s       ... 検索文字を指定"
    WScript.Quit
End Sub

' --< 引数: 文字列チェック関数 >-------------------------------------------------
Function opt_check(c_arg, i)
    opt_check = Flase
    ' -f,-sの次の文字列が存在するか?
    If(i >= c_arg.count) Then
        Exit Function
    End If

    ' 続けてオプション指定していないか?
    If(Left(c_arg(i), 1) = "-") Then
        Exit Function
    End If

    opt_check = True
    Exit Function
End Function

' --< 引数: フォルダチェック関数 >-----------------------------------------------
Function folder_check(c_arg, i)
    folder_check = True
    if(fso.FolderExists(c_arg(i)) = False) Then
        folder_check = False
    End If

    Exit Function
End Function

' --< メイン処理 >---------------------------------------------------------------
Set arg = WScript.Arguments
Set fso = CreateObject("Scripting.FileSystemObject")

srhFol = ""
srhStr = ""
For i=0 to arg.Count - 1
    Select Case arg(i)
        Case "-s"
            i = i + 1
            If(opt_check(arg, i) = Flase) Then
                err_sub
            End If
            srhStr = arg(i)
        Case "-f"
            i = i + 1
            If(opt_check(arg, i) = Flase) Then
                err_sub
            End If
            If(folder_check(arg, i) = Flase) Then
                WScript.Echo "指定のフォルダは存在しません"
                WScript.Quit
            End If
            srhFol = arg(i)
        Case Else
            err_sub
    End Select
Next

WScript.Echo srhFol
WScript.Echo srhStr

 な...長いですね。Level.10が60ステップ(60行)だったのに対し、引数のチェックだけで67ステップにもなってしまいました。このあたりが、(ワタシ的に)面倒だと言った理由になります。ただ、ロジック的にはさほど難しい訳ではありません。

 さて、説明ですが、このレベルはおまけということで、簡単に済ませたいと思います(汗。

メインロジック

 メインロジックは37行目からになります。まず、41,42行目で検索フォルダ/検索文字を格納する変数を初期化します。次に、43〜64行目のFor文で、引数を最初から最後までチェックする繰り返し処理を行っています。この時、arg(i)に今チェック中の値が入ることになります。

 44〜63行目では、Select文を使って多分岐判断処理を行っています。If文だと条件に対してYesかNo(TureかFalse)にしかなりませんが、Select文(多分岐判断)は、(44行目)Select Caseの後に書かれた変数の値が、Case(45,51行目)の後に書かれた値と一致した時に、その下に書かれた処理をしてくれるというものです。61行目のCase Elseは、どのCaseにも当てはまらなかった時に処理されます。

 余談ですが、引数の処理を行う繰り返し処理では、他の言語ではshift文を使ったりするのですが、VBScriptでは見あたりませんでした(あるのかなぁ??)。配列を処理する、pop,push見たいな機能でも良かったのですが、それも無いんですよねぇ...うーん。

-sオプション処理

 本題に戻りまして、オプションに-sが指定された時の処理を見てみましょう。-sオプションは検索文字列の指定になりまして、場所は46〜50行目になります。まず、-sオプションの次の文字列を判定したいのですから、arg(i)のiの値に1加算しています。次に、文字列チェックを行うopt_check関数を呼び出します。この関数の引数は、arg配列と、チェック対象となる配列上の場所、この場合ですと変数iを指定します。

 opt_check関数では、まず-sの次に文字列があるかチェックします(14行目)。これは、配列数の最大値を超えていないかで判断します。また、"-s -f"などとオプションを連続で指定されたことを想定し、-sの次の文字列が"-"で始まっていないかチェックしています。ただし、場合によっては"-f"という文字列を検索したいことも考えられますので、余計な処理かもしれません。

 この、いずれかのチェックに該当する場合(配列数の最大値を超えている場合、検索文字列が-で始まっている場合)エラーとし、関数の戻り値をFalseとしています。それ以外の場合は、戻り値をTrueとしています。

 処理はメイン処理に戻ってきまして(47行目)、opt_check関数の戻り値がFalse(引数が不正)の場合、err_sub関数を呼び出します。err_sub関数は、1〜8行目ですね。やっていることは簡単で、この関数の使い方を表示して、スクリプトを終了しているだけです。

 一方、opt_check関数の戻り値がTrue(正常)の場合には、srhStr変数に引数で指定された文字列を入れています。

-fオプション処理

 -fオプションには検索フォルダを指定します。基本的なチェックは-sオプションと同じくopt_check関数で行っています。

 基本的なチェックの他に-fオプションでは、指定されたフォルダが本当に存在するかをfolder_check関数にて行っています。

 folder_check関数は、FileSystemObjectのFolderExistsメソッドを使ってフォルダの存在チェックを行います。戻り値は、フォルダが存在していればTrueで、存在しなければFolaseです。

 メインロジックに戻りまして(56行目)、folder_check関数の戻り値がFalse(フォルダが存在しない)の場合に、「指定のフォルダは存在しません」と表示して、スクリプトを終了しています。フォルダが存在している場合には、引数の値をsrfFol変数に格納しています。

それ以外のオプションが指定された場合

 -s,-fオプション以外の文字列が指定された場合は、61行目のCase Else以下の処理を実行することになります。ここでは基本チェック同様、err_sub関数によりスクリプトの使い方を表示して、スクリプトを終了しています。

 とりあえず思った通りの動作をするか、arg.vbsを実行してみましょう。

arg.vbsファイルをコマンドラインから実行
C:\>cd c:\script
C:\script>cscript arg.vbs 1 2 3 4
使い方
cscript level10a.vbs -f <folder_name> -s <string>
-f <folder_name> ... 検索フォルダを指定
-s <string>      ... 検索文字を指定

C:\script>cscript arg.vbs -s "ファイル" -f "c:\script\tips"
c:\script\tips
ファイル

C:\script>

level10.vbsとarg.vbsの合体

 では、level10.vbsとarg.vbsを合体してみましょう。

引数チェック(arg.vbs)
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
120
121
122
123
124
125
' --< エラーメッセージ表示関数 >-------------------------------------------------
Sub err_sub()
    WScript.Echo "使い方"
    WScript.Echo "cscript level10a.vbs -f  -s "
    WScript.Echo "-f  ... 検索フォルダを指定"
    WScript.Echo "-s       ... 検索文字を指定"
    WScript.Quit
End Sub

' --< 引数: 文字列チェック関数 >-------------------------------------------------
Function opt_check(c_arg, i)
    opt_check = Flase
    ' -f,-sの次の文字列が存在するか?
    If(i >= c_arg.count) Then
        Exit Function
    End If

    ' 続けてオプション指定していないか?
    If(Left(c_arg(i), 1) = "-") Then
        Exit Function
    End If

    opt_check = True
    Exit Function
End Function

' --< 引数: フォルダチェック関数 >-----------------------------------------------
Function folder_check(c_arg, i)
    folder_check = True
    if(fso.FolderExists(c_arg(i)) = False) Then
        folder_check = False
    End If

    Exit Function
End Function

' --< テキストファイル処理関数 >-------------------------------------------------
' 指定のフォルダに格納された各ファイルを処理する
Sub FileSrh(fn)
    Set subf = fso.GetFolder(fn)    ' 引数に指定されたフォルダの情報を取得
    For Each FileName In subf.Files
        FileEx = fso.GetExtensionName(FileName)   ' ファイル名から拡張子を抜き出す
        If LCase(FileEx) = "txt" Then             ' 拡張子を小文字化してから比較
    
          ' テキストファイルの内容読み込み処理
            Set TextFile = fso.OpenTextFile(FileName) ' テキストファイルのオープン
            srhLine = FileName & ":" & vbNewLine      ' 検索結果格納用変数の初期化
            srhFlg = 0                                ' 検索ヒットフラグのクリア
            Do Until TextFile.AtEndOfStream
                tmpLine = TextFile.ReadLine           ' 1行読み込み
    
                If regEx.Test(tmpLine) Then           ' 指定の文字列を含むか?
                    srhLine = srhLine & TextFile.Line - 1 & ":" & tmpLine & vbNewLine
                                                      ' 読み込んだ行のマージ
                    srhFlg =1                         ' 検索がヒットしたので1にする
                End If
            Loop
    
            If srhFlg = 1 Then                        ' 指定の文字列が含まれてたら
                WScript.Echo srhLine
            End If
    
        End If
    Next

    ' 指定のフォルダに格納された各サブフォルダを処理する
    For Each FolderName In subf.Subfolders
        FileSrh(FolderName)
    Next
End Sub

' --< main >---------------------------------------------------------------------
Set arg = WScript.Arguments
Set fso = CreateObject("Scripting.FileSystemObject")

' 引数チェック処理
srhFol = ""
srhStr = ""
For i=0 to arg.Count - 1
    Select Case arg(i)
        Case "-s"
            i = i + 1
            If(opt_check(arg, i) = Flase) Then
                err_sub
            End If
            srhStr = arg(i)
        Case "-f"
            i = i + 1
            If(opt_check(arg, i) = Flase) Then
                err_sub
            End If
            If(folder_check(arg, i) = Flase) Then
                WScript.Echo "指定のフォルダは存在しません"
                WScript.Quit
            End If
            srhFol = arg(i)
        Case Else
            err_sub
    End Select
Next

' フォルダ名入力処理
If(srhFol = "") Then
    Do Until fso.FolderExists(srhFol)  ' 存在するフォルダ名が入力されるまで繰り返す
        tmpMsg = "検索フォルダを入力して下さい" & vbNewLine & _
                 "終了時は""c""と入力して下さい。"
        srhFol = InputBox(tmpMsg,,"C:\script\tips") ' 対象フォルダの指定
        If LCase(srhFol) = "c" Then     ' cが入力されたらスクリプトを終了する
            WScript.Quit
        End If
    Loop
    Set src = fso.GetFolder(srhFol)           ' 対象フォルダの指定
End If

' 検索文字入力処理
Set regEx = New RegExp                        ' 文字列検索用オブジェクトの作成
If(srhStr = "") Then
    srhStr = InputBox("検索文字を入力して下さい",,"ファイル") ' 検索文字列の指定
End If

regEx.Pattern = srhStr                        ' 検索文字列を検索パターンとして指定
regEx.Global = True                           ' 文字列全体を検索するように指定
regEx.IgnoreCase = True                       ' 大文字・小文字は検索に影響しない

' テキスト処理関数呼び出し
FileSrh(srhFol)

 行番号が青色になっている行がarg.vbsから持ってきた部分で、黒色が元々level10.vbsにあった行です。ピンクは修正もしくは追加した行となっています。

 青色の部分は基本的に、関数定義部分を最初に追加、引数チェック部分はメインロジックの先頭の方(オブジェクト定義の次)に追加しただけですので説明は省きます。

 新たに修正したり追加したピンクの部分ですが、引数に検索フォルダや検索文字が指定されなかった場合だけ、従来の検索フォルダ入力処理および、検索文字入力処理を行うようにIf文を追加しました。

 説明は以上になります。それにしても...改めて全ロジックを眺めて見ますと、長くなってますねぇ(汗

実行してみましょう

 では実行してみましょう。

level10a.vbsをコマンドラインから実行
C:\>cd c:\script
C:\script>cscript level10a.vbs 1 2 3 4
使い方
cscript level10a.vbs -f <folder_name> -s <string>
-f <folder_name> ... 検索フォルダを指定
-s <string>      ... 検索文字を指定

C:\script>cscript arg.vbs -s "ファイル" -f "c:\script\tips" > kekka.txt

C:\script>type kekka.txt
C:\script\tips\R23_001.txt:
25: スクリプトは、テキストファイルに書かれたプログラムです。よって、Windowsに標準
27:VBScriptの場合「.vbs」などとして保存する必要があります。また、テキストファイル
30: スクリプト(WSH)で何が出来るかといえば、ファイルのコピー、アプリケーションの立
〜

 と、こんな感じで利用することが出来るようになりました。

 さて、今回は「おまけ」と称しまして、コマンドライン引数で検索フォルダや検索文字を指定できるようにしてみましたが、いかがでしたでしょうか?

 次回からは、また本編に戻りますが、ユーザーインタフェース部分を改善するために、ダイヤログボックスからInternetExplorerを使ったものに修正して行きます。



前ページへ   [ステップアップ WSHへ戻る]   次ページへ
-
※全ては自己責任でお願いします。
※当サイトに関するご連絡は tomomo_c@hotmail.com までお願いします。
最終更新日 2004.6.16