最新版は以下に記載しています。
https://hana-shin.hatenablog.com/entry/2023/04/07/212030
#1 はじめに
Expectを使って業務を自動化したいと思っています。
Expectは、内部でTcl(Tool Command Language)を呼び出すことができます。
Wikiによると、ExpectはTclの拡張だそうです。
そのため、まず、Tclの使い方を理解するところから始めました。
Tclはスクリプト言語です。Tclは"ティクル"と発音するようです。
#2 環境
VMware Workstation 15 Player上の仮想マシンを使いました。
仮想マシンの版数は以下のとりです。
[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
[root@server ~]# uname -r
3.10.0-957.el7.x86_64
[root@server ~]# rpm -qa|grep tcl
tcl-8.5.13-8.el7.x86_64
#3 Hello world
##3.1 標準出力への出力方法(puts)
puts関数を使って、指定した文字列を標準出力に出力してみます。
なお、stdout
は省略可能です。
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
puts "Hello world!"
puts stdout "Hello world!!"
[root@server ~]# chmod 700 test.exp
[root@server ~]# ./test.exp
Hello world!
Hello world!!
##3.2 標準入力からの入力方法(gets)
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
while {1} {
set str [gets stdin]
puts stdout $str
}
[root@server ~]# ./test.exp
111 <= キーボードから111を入力する
111
222 <= キーボードから222を入力する
222
^C^C^C
^C
を押下しても終了しない。。。
^C
押下のあとEnter
キーを押下すると終了する。
なんでだろう。。
#4 変数の使い方(set)
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set var 123
puts $var
set var abc
puts $var
[root@server ~]# ./test.exp
123
abc
#5 制御文の使い方(if, elseif, else文)
##5.1 if文の使い方
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set var 1
if {$var == "1"} {
puts $var
}
[root@server ~]# ./test.exp
1
##5.2 else文の使い方
サンプルスクリプト内のlindexコマンドは、リストから要素を取り出すためのコマンドです。以下の例では、lindex $argv 0
は、最初の引数を意味しています。
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set var [lindex $argv 0]
if {$var == "1"} {
puts "1"
} else {
puts "other than 1"
}
[root@server ~]# ./test.exp 1
1
[root@server ~]# ./test.exp 2
other than 1
[root@server ~]# ./test.exp 3
other than 1
##5.3 elseif文の使い方
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set var [lindex $argv 0]
if {$var == "1"} {
puts "1"
} elseif {$var == "2"} {
puts "2"
} else {
puts "other than 1 or 2"
}
[root@server ~]# ./test.exp 1
1
[root@server ~]# ./test.exp 2
2
[root@server ~]# ./test.exp 3
other than 1 or 2
[root@server ~]# ./test.exp 4
other than 1 or 2
#6 繰り返しの使い方
##6.1 無限ループ
afterコマンドは、スリープする時間を指定できます。
ここでは、afterコマンドを使って、1秒間隔で現在時刻を表示してみます。
```console:スクリプト(while{1}
)
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
while {1} {
set x [exec date]
puts $x
after 1000;
}
```console:実行結果
[root@server ~]# ./test.exp
2020年 7月 16日 木曜日 16:46:08 JST
2020年 7月 16日 木曜日 16:46:09 JST
2020年 7月 16日 木曜日 16:46:10 JST
-snip-
##6.2 forを使った繰り返し
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
for {set x 0} {$x<3} {incr x} {
puts "x is $x"
}
[root@server ~]# ./test.exp
x is 0
x is 1
x is 2
##6.3 foreachを使った繰り返し
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
foreach i {xx yy zz} {
puts $i
}
[root@server ~]# ./test.exp
xx
yy
zz
#7 スクリプトの引数の使い方
$argv0
はスクリプトの名前(ここではtest.exp)、$argc
は引数の個数を表します。
$argv
は、スクリプトへの引数を表しています。
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set name $argv0
set num $argc
set x [lindex $argv 0]
set y [lindex $argv 1]
puts $name
puts $argc
puts $x
puts $y
スクリプトの名前はtest.exp
、引数の個数は2、
第1引数は111
、第2引数はaaa
であることがわかります。
[root@server ~]# ./test.exp 111 aaa
./test.exp
2
111
aaa
#8 関数の使い方
サンプルスクリプト中の[ ]
引用符はコマンド置換子です。
[ ]
で挟まれたリストはコマンドラインとみなされ、その実行結果がひとつのアーギュメントとしてコマンドに渡されます。
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set x 1
set y 2
proc add {x y} {
puts x=$x,y=$y
return [expr $x + $y]
}
set ret [add 1 2]
puts x+y=$ret
[root@server ~]# ./test.exp
x=1,y=2
x+y=3
#9 stringコマンドの使い方
##9.1 小文字、大文字を区別する場合
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set var [lindex $argv 0]
if ![string compare $var "aaa"] {
puts "equal"
} else {
puts "not equal"
}
[root@server ~]# ./test.exp aaa
equal
[root@server ~]# ./test.exp AAA
not equal
[root@server ~]# ./test.exp bbb
not equal
##9.2 小文字、大文字を区別しない場合(-nocase)
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set var [lindex $argv 0]
if ![string compare -nocase $var "aaa"] {
puts "equal"
} else {
puts "not equal"
}
小文字、大文字が区別されないことがわかります。
[root@server ~]# ./test.exp aaa
equal
[root@server ~]# ./test.exp AAA
equal
[root@server ~]# ./test.exp bbb
not equal
##9.3 ファイルから文字列を読み込む方法
[root@server ~]# vi sample.txt
[root@server ~]# cat sample.txt
192.168.3.10 server
192.168.3.20 client
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set filename "sample.txt"
set fd [open $filename]
while {! [eof $fd]} {
gets $fd line
puts stdout $line
}
close $fd
[root@server ~]# ./test.exp
192.168.3.10 server
192.168.3.20 client
##9.4 文字列を分割する方法
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set src "000 111 222"
set dst [split $src " "]
puts [lindex $dst 0]
puts [lindex $dst 1]
puts [lindex $dst 2]
[root@server ~]# ./test.exp
000
111
222
#10 日時を表示する方法
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set systemTime [clock seconds]
puts "Year with 4 digits is [clock format $systemTime -format %Y]"
puts "Month number(01-12) is [clock format $systemTime -format %m]"
puts "Day of month is [clock format $systemTime -format %d]"
puts "Hour(00-23) is [clock format $systemTime -format %H]"
puts "Minutes(00-59) is [clock format $systemTime -format %M]"
puts "Seconds(00-59) is [clock format $systemTime -format %S]"
puts "Now is [clock format $systemTime -format %Y/%m/%d,%H:%M:%S]"
[root@server ~]# date
2020年 7月 16日 木曜日 16:17:19 JST
[root@server ~]# ./test.exp
Year with 4 digits is 2020
Month number(01-12) is 07
Day of month is 16
Hour(00-23) is 16
Minutes(00-59) is 17
Seconds(00-59) is 21
Now is 2020/07/16,16:17:21
#11 外部コマンドを実行する方法(exec)
execを実行することで、子プロセスを起動します。
ここでは、execによって、date
,ls
,cal
コマンドを実行してみます。
##11.1 時刻を表示する方法
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set x [exec date]
puts $x
[root@server ~]# ./test.exp
2020年 7月 16日 木曜日 16:26:02 JST
##11.2 ファイル、ディレクトリを表示する方法
ディレクトリのファイル一覧を表示してみます。
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set x [exec ls -l]
puts $x
[root@server ~]# ./test.exp
合計 615664
-rw-------. 1 root root 1828 2月 14 17:48 anaconda-ks.cfg
-rw-r--r--. 1 root root 117272 7月 4 2014 bc-1.06.95-13.el7.x86_64.rpm
drwxr-xr-x. 5 root root 42 4月 11 11:47 bin
-snip-
##11.3 カレンダーを表示する方法
カレンダーを表示してみます。
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set x [exec cal]
puts $x
[root@server ~]# ./test.exp
7月 2020
日 月 火 水 木 金 土
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
#12 配列の使い方
##12.1 配列に値を代入/配列要素の値を表示する方法
x
という名前の配列を作成します。要素数は3です。
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set x(0) aaa
set x(1) bbb
set x(2) ccc
puts $x(0)
puts $x(1)
puts $x(2)
[root@server ~]# ./test.exp
aaa
bbb
ccc
##12.2 配列の要素数を求める方法(array size
)
要素数3の配列を作成します。
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set x(0) aaa
set x(1) bbb
set x(2) ccc
set n [array size x]
puts "array has $n elements"
配列xの要素数が3であることがわかります。
[root@server ~]# ./test.exp
array has 3 elements
##12.3 配列が存在するかどうかを確認する方法(array exists
)
配列が存在するかどうかは、array exists
コマンドを使って確認することができます。
配列が存在する場合は1,配列が存在しない場合は0を返します。
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set x(0) aaa
set x(1) bbb
puts $x(0)
puts $x(1)
puts "Does array exist? [array exists x]"
array unset x
puts "array is deleted by unset command"
puts "Does array exist? [array exists x]"
[root@server ~]# ./test.exp
aaa
bbb
Does array exist? 1
array is deleted by unset command
Does array exist? 0
##12.4 指定した添字の要素を返す方法(array get
)
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set x(0) aaa
set x(1) bbb
set x(2) ccc
puts [array get x]
puts [array get x 0]
puts [array get x 1]
puts [array get x 2]
[root@server ~]# ./test.exp
0 aaa 1 bbb 2 ccc
0 aaa
1 bbb
2 ccc
##12.5 2次元配列の使い方
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set x(0,0) 10
set x(0,1) 20
set x(1,0) aa
set x(1,1) bb
for {set i 0} {$i < 2} {incr i} {
for {set j 0} {$j < 2} {incr j} {
puts $x($i,$j)
}
}
[root@server ~]# ./test.exp
10
20
aa
bb
##12.6 連想配列の使い方
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set x("tokyo") 14000000
set x("osaka") 8900000
puts $x("tokyo")
puts $x("osaka")
[root@server ~]# ./test.exp
14000000
8900000
#13 リストの使い方
リストとは、中括弧あるいはダブルクォートを使ってスペースで区切られた文字列のことです。
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set x {10 20 30}
puts [lindex $x 0]
puts [lindex $x 2]
puts "The length of list x is [llength $x]"
[root@server ~]# ./test.exp
10
30
The length of list x is 3
#14 ファイル操作
##14.1 ファイルが存在するかどうかを確認する方法
カレントディレクトリにsample.txt
が存在するかどうかを確認する
スクリプトを作成します。
[root@server ~]# touch sample.txt
[root@server ~]# ls sample.txt
sample.txt
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
if {[file exists sample.txt] == 1} then {
puts "FOUND"
} else {
puts "NOT FOUND"
}
[root@server ~]# ls sample.txt
sample.txt
[root@server ~]# ./test.exp
FOUND
[root@server ~]# ls sample.txt
ls: sample.txt にアクセスできません: そのようなファイルやディレクトリはありません
[root@server ~]# ./test.exp
NOT FOUND
##14.2 ファイル名のドットまでの名前を返す方法
最後のドット(最後のドットを含まない)までのファイル名を返します。
[root@server ~]# touch sample.tar.gz
[root@server ~]# ls sample.tar.gz
sample.tar.gz
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set x [file rootname sample.tar.gz]
puts $x
set y [file rootname $x]
puts $y
[root@server ~]# ./test.exp
sample.tar
sample
##14.3 ディレクトリを作成する方法
test
ディレクトリが存在しないことを確認します。
[root@server ~]# ls -ld test
ls: test にアクセスできません: そのようなファイルやディレクトリはありません
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set test_dir "/root/test"
file mkdir $test_dir
test
ディレクトリが作成されたことを確認します。
[root@server ~]# ./test.exp
[root@server ~]# ls -ld test
drwxr-xr-x. 2 root root 6 7月 16 21:50 test
#15 変数の有効範囲
globalコマンドを使うことで、関数外の変数にアクセスすることができます。
globalコマンドは、関数内で実行する必要があります。
ここでは、関数test1内で、変数xをグローバル変数として定義しています。
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
set x 10
proc test1 {} {
global x
return $x
}
proc test2 {} {
set x abc
return $x
}
puts "The return value of test1 is [test1]"
puts "The return value of test2 is [test2]"
[root@server ~]# ./test.exp
The return value of test1 is 10
The return value of test2 is abc
#16 sourceの使い方
sourceを使って、外部のtclソースファイルを読み込んでみます。
source - Evaluate a file or resource as a Tcl script
##16.1 サンプルスクリプト
[root@server ~]# cat test.exp
#!/usr/bin/expect
source proc.exp
test
[root@server ~]# cat proc.exp
proc test {} {
puts "This is sample"
}
[root@server ~]# ./test.exp
This is sample
#z 参考情報
Tclの文法(大変参考になりました!)
文法とコマンド
Tcl 8.4.1 Manual Command Reference
Tcl/Tk お気楽 GUI プログラミング応用編
コマンドラインと特殊文字
Tcl – 外部プログラムの実行について -exec-
説明のないとってもシンプルなサンプルプログラム集
Tcl-tk-tcl-error-handling
Tcl – 配列(1) -配列の作成・更新-
Tcl 8.4.1 Manual Command Reference
TCL-Arrays with Examples
signal