TeraTermマクロはwaitregexなどいくつかのTTLコマンドで正規表現を使う事ができます。正規表現を使う事により、より柔軟なマクロを書く事ができるようになります。
タップできる目次
正規表現とは
正規表現とは文字列の集合をひとつの文字列で表現するものです。たとえばアルファベットの集合なら[a-zA-Z]と表現する事ができます。このようにある文字列をひとつの文字列で表現する事により柔軟に特定の文字列にマッチさせる事ができるようになります。
正規表現に対応したTTLコマンド
- strmatch
- strreplace
- waitregex
TeraTermマクロの正規表現
TeraTermマクロでは「鬼車」という正規表現ライブラリを使用しています。TeraTermマクロで正規表現を使いこなすためには、この鬼車の使い方を熟知する必要があります。
主な鬼車の文法
正規表現は文字コードに依存します。ここではShiftJISを対象としています。
基本要素
¥ | エスケープ |
| | 選択子 |
(…) | グループ化 |
[…] | 文字クラス |
文字
¥t | 水平タブ |
¥v | 垂直タブ |
¥n | 改行 |
¥r | 復帰 |
¥b | 後退空白 |
¥f | 改頁 |
¥a | ベル |
¥e | 退避修飾 |
¥nnn | 8進数表現 |
¥xHH | 16進数表現 |
文字種
. | 改行を除く任意の1文字 |
¥w | 単語構成文字 |
¥W | 非単語構成文字 |
¥s | 空白文字 |
¥S | 非空白文字 |
¥d | 10進数数字 |
¥D | 非10進数文字 |
¥h | 16進数文字 |
¥H | 非16進数文字 |
量指定子
欲張り
? | 1回または0回 |
* | 0回以上 |
+ | 1回以上 |
{n,m} | n回以上m回以下 |
{n,} | n回以上 |
{,n} | 0回以上n回以下 |
{n} | n回 |
無欲
?? | 1回または0回 |
*? | 0回以上 |
+? | 1回以上 |
{n,m}? | n回以上m回以下 |
{n,}? | n回以上 |
{,n}? | 0回以上n回以下 |
強欲
?+ | 1回または0回 |
*+ | 0回以上 |
++ | 1回以上 |
アンカー
^ | 行頭 |
$ | 行末 |
¥b | 単語境界 |
¥B | 非単語境界 |
¥A | 文字列先頭 |
¥Z | 文字列末尾 文字列末尾の改行直前 |
¥z | 文字列末尾 |
¥G | 照合開始位置 |
文字集合
文字集合([…])の中で使われる
^… | 否定(最低優先度) |
x-y | 範囲(xからyまで) |
[…] | 集合 |
..&&.. | 積演算(^の次に優先度が低い) |
POSIXブラケット
[[:xxxx: ]]形式で使用する。否定の場合は[[:^xxxx:]]形式で使用する。alnum | 英数字 |
alpha | 英字 |
ascii | 0-127 |
blank | t x20 |
digit | 0-9 |
graph | 多バイト文字すべて |
lower | 小文字 |
印字可能文字 多バイト文字すべて |
|
space | ¥t ¥n ¥v ¥f ¥r ¥x20 |
upper | 大文字 |
xdigit | 0-9 |
word | 英数字 “_” および 多バイト文字 |
正規表現の使用例
それでは、せっかくなので実用的な正規表現例をご紹介します。
Cisco機器のログ取得でうまくマッチしない!
という例はよく目にします。これは大抵正規表現が甘いためです。たとえば show running-config を実行したとします。そういう場合、どのような正規表現を使うか。waitregexを使うと次のような感じです。
waitregex “Switch”
これは一見うまく行くように見えます。しかし、show running-configには次のような文字列が存在します。
hostname Switch
さきほどの正規表現だと上記の文字列にマッチしてしまいますね。このように単純にホスト名だけでマッチさせると意図しない場所でマッチします。Cisco機器の場合、ホスト名を「Switch」とすると次のようなプロンプトが存在します。
- Switch>
- Switch#
- Switch(config)#
- Switch(config-line)#
- Switch(config-if)#
- Switch(config-if-range)#
- Switch(config-vlan)#
- Switch(vlan)#
他にもありますが、ここでやめておきましょう。このように様々な文字列をすべて調べてwaitregexを使う、というのは現実的ではありません。しかし、このような様々な文字列でも、正規表現ならばひとつの文字列で表現できます。正規表現を使う場合は文字列のパターンを探します。
文字列パターンを探す
Cisco機器のプロンプトには次のようなパターンがあります。
- ホスト名に続いて「>」もしくは「#」
- ホスト名に続いて「(config)#」
- ホスト名に続いて「(config-アルファベット)#」
- ホスト名に続いて「(config-アルファベット-アルファベット)#」
- ホスト名に続いて「(vlan)#」
正規表現の文字列作成
まず最初にホスト名に続いて「>」もしくは「#」のパターンを書きます。
^Switch[>#]$
これで、行頭が「Switch」、その後に「>」もしくは「#」があり、続いて行末がある場合にマッチします。
次は「(config)」がある場合を検討します。
^Switch¥(config¥)[>#]$
上記の正規表現でも良さそうに見えますが「(config)」がないとマッチしません。ですので「(config)」は有っても無くても良いよ、という意味の「?」を使います。
^Switch(¥(config¥))?[>#]$
だいぶそれらしくなりました。「(…)?」の部分は有っても無くても良いので、「Switch>」「Switch#」「Switch(config)#」にマッチします。
このように「?」を使うと有っても無くても良いという意味になるので「config」「config-アルファベット」「config-アルファベット-アルファベット」「vlan」すべてにマッチさせるためには、次のような正規表現を使います。
^Switch(¥(([a-z]+(-.+)?)?¥))?[>#]$
この正規表現が実際にマッチするのか、テスト用のマクロを書きました。
; テスト用文字列 strdim str 9 str[0] = "Switch>" str[1] = "Switch#" str[2] = "Switch(config)#" str[3] = "Switch(config-line)#" str[4] = "Switch(config-if)#" str[5] = "Switch(config-if-range)#" str[6] = "Switch(config-vlan)#" str[7] = "Switch(vlan)#" str[8] = "hostname Switch" ; 正規表現 regexstr = "^Switch(¥(([a-z]+(-.+)?)?¥))?[>#]$" for i 0 8 strmatch str[i] regexstr if result == 0 then sprintf2 msg "マッチしないn%s" str[i] title = "マッチしない" else sprintf2 msg "マッチした!!n%s" str[i] title = "マッチした!!" endif strspecial msg messagebox msg title next
どの文字列にマッチして、どの文字列にはマッチしないのか実際に試してみてください。
いつも参考にさせていただいております。
「Switch」の部分をユーザが取得した変数(下記のHOST_NAME)に変えて、正規表現を組み合わせることは可能でしょうか。
※Cisco機器のみを想定しています。
ホスト名は機器ごとに変わるため、実環境では「任意のホスト名+プロンプトを示す正規表現」といったかたちでマクロを作りたいです。
任意のホスト名を取得するのは、特権モードに入っている状態で下記を実行しています。
※ter len 0を実行して、表示されたホスト名+プロンプト#から、#を分離することで、ホスト名のみを取得して、それをユーザ変数に格納しています。
sendln ‘ter len 0’
mpause 500
waitregex ‘(.*)\#’
HOSTNAME = matchstr
strsplit HOSTNAME ‘#’
HOST_NAME = groupmatchstr1
two23さん
返信遅れてすみません。
Tera Termマクロのやり方ですと、現状ではsprintf2を使う書き方が良いかと思います。
※他に良い感じのやり方を知っている方は、コメントして頂けると嬉しいです。
sprintf2 wait_str “^%s(\(([a-z]+(-.+)?)?\))?[>#]$” HOST_NAME
waitregex wait_str
shjさん
初めまして、まーくんと申します。
教えてください。sprintf2~のマクロは、two23さんのマクロのどこに
入れればよいのでしょうか?もしくは、どこを修正すればよいのでしょうか?
教えてください。
任意のホスト名+プロンプト(#)から、ログ収集のファイル名を
作成したいためです。
以上
まーくんさん
スパムに埋もれていて返信が遅れてしまいました。すみません。
two23さんのマクロで変数「HOST_NAME」にホスト名を格納できるという事ですので、その値を使ってwaitregxでプロンプトを待ち受ける正規表現文字列を作成します。具体的には一番下に付けます。以下のように:
sendln ‘ter len 0’
mpause 500
waitregex ‘(.*)\#’
HOSTNAME = matchstr
strsplit HOSTNAME ‘#’
HOST_NAME = groupmatchstr1
; ↓に追加
sprintf2 wait_str “^%s(\(([a-z]+(-.+)?)?\))?[>#]$” HOST_NAME
waitregex wait_str
ホスト名の取得方法については、このサイトでも記事を書いていたので念のためコメントに残しておきます:
http://teraterm.jp/?p=978
shjさん
問い合わせの返信ありがとうございます。
また何かありましら、問い合わせをさせて頂きます。
ご回答ありがとうございます。
こちらでも試してみた結果、strconcatでも出来ました。
下記で問題なく動作していること確認しております。
WAIT_HOST_NAME = HOST_NAME
strconcat WAIT_HOST_NAME ‘(\(([a-z]+(-.+)?)?\))?[>#]$’
sendln ‘conf t’ ※コマンドは任意
waitregex WAIT_HOST_NAME
strconcatで問題なく動いているという事で、何よりです。
厳密なことを言うと、two23さんの記載されたマクロですと「^」がないため先頭一致をチェックできませんから、注意が必要です。たとえば、hoge.com#でマッチさせるはずがhogehoge.com#でもマッチしてしまいます。よくあるパターンでは、ディスクリプションやログに含まれている文字列にマッチします。
ただ、現状問題なく動いているのであれば、そこまで気にする必要は無いかも知れません。
ご回答ありがとうございます。
確かに先頭一致がないので、ディスクリプションなどでマッチする危険性?がありますね。sprintf2でも試してみます。
またホスト名が一定以上の長さの場合、プロンプトによってはホスト名の後半が省略されたかと思うので、それにも対応できるようにしようと考えています。