←前
目次
次→

Luaスクリプト機能

$Date: 2009/10/13 02:58:59 $

Luaスクリプトの基礎

次に、もう少し役に立つ例題として、指定したIPアドレスにpingを打ち、その結果をログに記録するスクリプトを取り上げてみましょう。

ここでは、例として提示するスクリプトはすべてファイルに保存してあるものを実行する形とします。

ルーターのコマンドを実行する

Luaスクリプト機能では、ルーターの通常のコマンドをスクリプトから実行するための、rt.command関数をライブラリとして用意しています。ですから、rt.command関数を使ってルーターのpingコマンドを呼び出せば、pingを実行することができます。

[ping1.lua] ダウンロード

flag, outputs = rt.command("ping -c 1 192.168.100.100")
if flag then
  first_line = string.match(outputs, "^(.-)\n")
  print(first_line)
end
    
# lua /lua/ping1.lua
received from 192.168.100.100: icmp_seq=0 ttl=63 time=1.288ms
    

どうでしょうか。pingできたでしょうか。なお、宛先のIPアドレスへ到達不能なときは何も表示されないので、その場合は「192.168.100.100」を到達可能なIPアドレスへ書き換えてから試してください。

それでは、スクリプトを解説していきます。まず、1行目では、ルーターのpingコマンドを実行しています。

flag, outputs = rt.command("ping -c 1 192.168.100.100")
    

rt.command関数は、引数として与えた文字列をルーターのコマンドと解釈して実行します。また、rt.command関数は戻り値として2つの値を返します。一番目の戻り値は、文字列がコマンドとして正しく解釈でき、コマンドを実行したかどうかを表します。二番目の戻り値はコマンドの出力結果もしくはエラーメッセージです。この例ではそれぞれを変数flagoutputsに代入しています。

2行目では、if文で変数flagの値を調べています。

if flag then
    

if文では、ifthenの間にある式の値が真であるときに、thenからendまでの間にある文を実行します。この例では式はflagだけなので、rt.command関数の実行結果となり、コマンドの書式が正しく、コマンドが実行されていれば真となります。

3行目では、pingコマンドの出力結果の一行目だけを抜き出しています。

  first_line = string.match(outputs, "^(.-)\n")
    

outputs変数にはpingコマンドの出力結果が文字列としてすべて含まれているので、そこから一行目だけを抜き出すために、string.match関数を利用しています。そして、抜き出した結果を変数first_lineに代入しています。

string.match関数の詳しい使い方はライブラリ関数の解説を見ていただきたいのですが、ここでは、この例のように書けば一行目が抜き出せるということで了解しておいてください。

4行目は、結果の表示です。

  print(first_line)
    

print関数を使って結果を表示しています。print関数はこのように文字列を簡単に表示することができます。

最後の5行目は、2行目のif文の終わりを示すend文です。

end
    

コマンド引数の利用

このスクリプトでは、pingする相手が「192.168.100.100」に固定されています。次は、luaコマンド実行時にIPアドレスを指定できるようにしてみましょう。

[ping2.lua] ダウンロード

dst = arg[1]
flag, outputs = rt.command("ping -c 1 " .. dst)
if flag then
  first_line = string.match(outputs, "^(.-)\n")
  print(first_line)
end
    
# lua /lua/ping2.lua 192.168.100.200
received from 192.168.100.200: icmp_seq=0 ttl=63 time=1.288ms
    

ping2.luaでは、luaコマンドでスクリプトを動かしたときに、引数でIPアドレスを指定するようにしました。スクリプトの1行目では、変数dstに1番目の引数を代入しています。

dst = arg[1]
    

argはコマンドで指定された引数を表す変数で、arg[1]で最初の引数、arg[2]で2番目の引数、arg[N]でN番目の引数となります。

2行目はpingコマンドの実行です。

flag, outputs = rt.command("ping -c 1 " .. dst)
    

ping1.luaとは、rt.command関数に与える引数が変わっています。"ping -c 1 "という文字列と変数dstを、..という記号で連結して一つの文字列としています。

..という記号は文字列連結演算子と呼ばれ、この記号の前後にある2つの文字列を連結して一つの文字列を作ることができます。

3行目以降は、ping1.luaとまったく同じです。

if〜then〜else

さて、ping2.luaでは、luaコマンドの引数に正しいIPアドレスが指定されることを前提としていますが、IPアドレスとして間違った形式の引数を与えたらどうなるでしょう?

# lua /lua/ping2.lua 192.168.100.2000
    

何も表示されませんね。これは、pingコマンドを実行しているrt.command関数が文法エラーで失敗しているためです。では、その場合にも何か表示してみましょう。

[ping3.lua] ダウンロード

dst = arg[1]
flag, outputs = rt.command("ping -c 1 " .. dst)
if flag then
  first_line = string.match(outputs, "^(.-)\n")
  print(first_line)
else
  print("ERROR: ping -c 1 " .. dst)
end
    

さて、今度はどうでしょうか。

# lua /lua/ping3.lua 192.168.100.2000
ERROR: ping -c 1 192.168.100.2000
    

エラー文字列が表示されましたね。ping3.luaでは、ping2.luaに対して、6行目、7行目のelse節を付け加えています。

if flag then
  first_line = string.match(outputs, "^(.-)\n")
  print(first_line)
else
  print("ERROR: ping -c 1 " .. dst)
end
    

if文では、ifthenの間にある式の値が真であるときに、then以降の文を実行します。また、else以降の文は、式の値が偽であるときに実行されます。今回は、pingコマンドが文法エラーとなり、rt.command関数の戻り値であるflagfalseとなるため、if文はelse節を実行してエラーメッセージを表示するということになります。

関数の定義と呼び出し

ping3.luaでは、コマンドラインで指定できるIPアドレスは一つだけでした。今度は、複数の相手へpingを打てるようにしましょう。

[ping4.lua] ダウンロード

function ping(dst)
  flag, outputs = rt.command("ping -c 1 " .. dst)
  if flag then
    first_line = string.match(outputs, "^(.-)\n")
    return first_line
  else
    return "ERROR: ping -c 1 " .. dst
  end
end

for i = 1, #arg do
  print(ping(arg[i]))
end
    

では、ping4.luaを実行してみましょう。

# lua /lua/ping4.lua 192.168.100.200 192.168.100.201 192.168.100.202
received from 192.168.100.200: icmp_seq=0 ttl=63 time=1.288ms
received from 192.168.100.201: icmp_seq=0 ttl=63 time=1.563ms
received from 192.168.100.202: icmp_seq=0 ttl=63 time=1.435ms
    

ping4.luaでは、ping3.luaとほぼ同じものが、function ping(dst)endで囲まれています。このように、コードの塊をfunctionendで囲んだものを、関数といいます。今回の場合、関数にはpingという名前が付けられており、後でこの名前を使って関数を呼び出すことで、コードの塊を実行することができます。

関数は、functionendで定義された後であれば、いつでも呼び出せます。ping4.luaでの関数定義と関数呼び出しの関係は以下のようになっています。

-- 関数定義
function ping(dst) flag, outputs = rt.command("ping -c 1 " .. dst) if flag then first_line = string.match(outputs, "^(.-)\n") return first_line else return "ERROR: ping -c 1 " .. dst end end
for i = 1, #arg do print(ping(arg[i])) -- 関数呼び出し end

関数呼び出しでは、arg[i]を実引数として関数pingに渡しています。arg[i]の値は、関数定義の方にある仮引数dstに代入され、関数が実行されます。

arg[i]dstはまったく違う変数であり、関数の実行を始める前に、dstarg[i]の値を代入しているだけ、という動作になっています。そのため、関数の実行中にdstの値を変更しても、関数呼び出し側のarg[i]には何の変化もありません。

関数定義の中で、return文があると、そこで処理が関数を呼び出した側に戻ります。そのときに、returnの後に続けられた式の値が、関数の値として呼び出し側に渡されます。ping4.luaでは、pingは成功、失敗いずれの場合も文字列を戻り値として返しており、呼び出し側ではそれをprintで出力しています。

コードの塊を関数として定義するのには、いくつかの理由があります。一つには、同じ処理が何度も現れる場合には、それを関数として定義して、必要な場合に呼び出すようにする、というものがあります。同じ処理を一箇所に集めることで、同じ処理をしているということが明らかになり、変更が必要になったときに変更箇所を減らすことができます。

もう一つの理由としては、処理に名前をつけて意味を明らかにする、ということがあります。今回の例では、ping動作をしているコードにpingという名前をつけました。これにより、後でこの関数を使うときには、ping動作をしているということがはっきりと分かるようになります。

処理を適切な単位で関数にまとめることは、人にコードを説明するときや、後でコードを保守しなくてはいけないときに大変役に立ちます。積極的に関数を使うようにしましょう。

ループ

ping4.luaでは、コマンドラインで渡された複数のIPアドレスを処理するために、for文によるループを使っています。関係部分を以下に抜き出します。

for i = 1, #arg do
  print(ping(arg[i]))
end
    

このfor文では、変数iの値を1から#argまで1ずつ増やしながら、doendの間、つまり、print(ping(arg[i]))を実行しています。#は配列の大きさや文字列の長さを返す長さ演算子であり、#argは配列argの大きさを表しているので、このfor文では、コマンドラインで与えられたIPアドレスに対して順に関数pingを呼び出し、その戻り値を出力している、ということになります。

このように、繰り返し実行を行うための文には、for文の他に、while文、repeat-until文があります。同じ意味のコードを、for文、while文、repeat-until文でそれぞれ書くと、以下のようになります。

for i = 1, #arg do
  print(ping(arg[i]))
end
    
i = 1
while i <= #arg do
  print(ping(arg[i]))
  i = i + 1
end
    
if #arg > 0 then
  i = 1
  repeat
    print(ping(arg[i]))
    i = i + 1
  until i > #arg
end
    

while文は、条件式が成立している間、repeat-until文は、条件式が成立していない間、それぞれのブロックを実行します。repeat-until文は、ブロックの実行後に条件式をチェックするため、必ず一度はブロックが実行されます。よって、上の例では全体をif文で囲み、#argが0、つまり、コマンドで引数が与えられなかった場合にはrepeat-until文が実行されないようにしています。

for文、while文、repeat-until文は同じように繰り返しを行うことのできる文ですが、それぞれ利用するのに適した場面があるので、適切な繰り返し文を使うようにしましょう。


←前 目次
次→