hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

Tclの使い方



1 Tcl(Tool Command Language)とは?

ソフトウェアのテスト工程を自動化するために、Tclとexpectについて調査しました。この記事では、Tclの使い方をまとめています。Tclはスクリプト言語で、「ティクル」と発音します。一方、Expectはリモートシステムとの対話的なプログラムを自動化するためのプログラムです。ExpectのスクリプトからTclを呼び出して、処理を実行します。Expectについては、別途記事を作成する予定です。

2 検証環境

AlmaLinux版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 8.6 (Sky Tiger)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
4.18.0-372.9.1.el8.x86_64

3 インストール方法

tclパッケージをインストールします。

[root@server ~]# dnf -y install tcl

tclパッケージの版数を確認します。

[root@server ~]# rpm -qa|grep tcl
tcl-8.6.8-2.el8.x86_64

4 putsコマンドの使い方

putsコマンドは文字列を標準出力やファイルに出力するコマンドです。

標準出力に文字列を出力するスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh
puts "Hello world!"
puts stdout "Hello world!!"

スクリプトを実行すると、標準出力に文字列が出力されていることがわかります。

[root@server ~]# ./test.exp
Hello world!
Hello world!!

5 getsコマンドの使い方

getsコマンドは、標準入力からユーザーが入力した文字列を取得するコマンドです。

標準入力からユーザーが入力した文字列をsetコマンドで変数strに設定したあと、putsコマンドで変数strの内容を標準出力に繰り返し出力するスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

while {1} {
  set str [gets stdin]
  puts stdout $str
}

スクリプトを実行すると、キーボードから入力した111,222が標準出力に出力されていることがわかります。スクリプトを終了するには、Ctrl+cを押下します。

[root@server ~]# ./test.exp
11111   <= キーボードから111を入力する
11111
22222   <= キーボードから222を入力する
22222
^C

6 setコマンドの使い方

setコマンドは変数に値を設定するコマンドです。

変数varに設定した文字列を標準出力に出力するスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

set var 123
puts $var

set var abc
puts $var

スクリプトを実行すると、変数varに設定した文字列(123,abc)が表示されていることがわかります。

[root@server ~]# ./test.exp
123
abc

7 制御コマンドの使い方

7.1 if,else,elseifの使い方

7.1.1 ifコマンドの使い方

変数varの値が1なら、oneと表示するスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

set var 1

if {$var == "1"} {
  puts "one"
}

スクリプトを実行すると、oneと表示されていることがわかります。

[root@server ~]# ./test.exp
one
7.1.2 elseコマンドの使い方

lindexコマンドはリストから要素を取り出すためのコマンドです。lindex $argv 0は第1引数を示します。引数に1を指定するとone、1以外を指定するとothersと表示するスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

set var [lindex $argv 0]

if {$var == "1"} {
  puts "one"
} else {
  puts "others"
}

スクリプトを実行すると、引数に1を指定するとone、1以外を指定するとothersと表示されていることがわかります。

[root@server ~]# ./test.exp 1
one
[root@server ~]# ./test.exp 2
others
[root@server ~]# ./test.exp 3
others
7.1.3 elseifコマンドの使い方

引数に1を指定するとone、2を指定するとtwo、1,2以外を指定するとothersと表示するスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

set var [lindex $argv 0]

if {$var == "1"} {
  puts "one"
} elseif {$var == "2"} {
  puts "two"
} else {
  puts "others"
}

スクリプトを実行すると、引数に1を指定するとone、2を指定するとtwo、1,2以外を指定するとothersと表示されていることがわかります。

[root@server ~]# ./test.exp 1
one
[root@server ~]# ./test.exp 2
two
[root@server ~]# ./test.exp 3
others

7.2 whileの使い方

setコマンドでdateの実行結果を変数xに設定したあと、putsコマンドで変数xの値を出力します。そして、1秒スリープします。これを繰り返すスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

while {1} {
  set x [exec date]
  puts $x
  after 1000;
}

スクリプトを実行すると、1秒間隔で時刻が表示されていることがわかります。Ctrl+cを押下してスクリプトを終了します。

[root@server ~]# ./test.exp
2023年  4月  4日 火曜日 20:21:59 JST
2023年  4月  4日 火曜日 20:22:00 JST
2023年  4月  4日 火曜日 20:22:01 JST
^C

7.3 forを使った繰り返し

変数xに初期値0を設定し、変数xを+1ずつ増加させ、変数xが3未満になるまで変数xの値を表示するスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

for {set x 0} {$x<3} {incr x} {
    puts "x is $x"
}

スクリプトを実行すると、0,1,2と表示されていることがわかります。

[root@server ~]# ./test.exp
x is 0
x is 1
x is 2

7.4 foreachを使った繰り返し

変数iにxxを設定したあと、putsコマンドで変数iの値を出力するスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

foreach i {xx yy zz} {
    puts $i
}

スクリプトを実行します。

[root@server ~]# ./test.exp
xx
yy
zz

8 引数の使い方

以下の変数を使って、引数を確認するスクリプトを作成します。
・$argv0:スクリプトの名前
・$argc:引数の個数
・$argv 0:第1引数を示す。lindexを使って第1引数を返します。
・$argv 1:第2引数を示す。lindexを使って第2引数を返します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

puts $argv0
puts $argc
puts [lindex $argv 0]
puts [lindex $argv 1]

スクリプトを実行すると以下のことがわかります。
スクリプトの名前:test.exp
・引数の個数:2
・第1引数:111
・第2引数:222

[root@server ~]# ./test.exp 111 222
./test.exp
2
111
222

9 関数の使い方

スクリプトの[ ]引用符はコマンド置換子です。[ ]で挟まれたリストはコマンドラインとみなされ、その実行結果がひとつのアーギュメントとしてコマンドに渡されます。

[root@server ~]# vi test.exp
[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

10 stringコマンドの使い方

10.1 大文字/小文字を区別する方法(string compare)

stringコマンドのcompareオプションを使って、大文字/小文字を区別するスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

set var [lindex $argv 0]

if ![string compare $var "aaa"] {
  puts "equal"
} else {
  puts "not equal"
}

スクリプトを実行すると、スクリプトへの引数がaaaならequal、AAAならnot equalと表示されていることがわかります。

[root@server ~]# ./test.exp aaa
equal
[root@server ~]# ./test.exp AAA
not equal

10.2 大文字/小文字を区別しない方法(string compare -nocase)

stringコマンドのcompareオプションに-nocaseを指定して、大文字/小文字を区別しないスクリプトを作成します。

[root@server ~]# vi test.exp
[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"
}

スクリプトを実行すると、引数がaaaでもAAAでもequalと表示されていることがわかります。

[root@server ~]# ./test.exp aaa
equal
[root@server ~]# ./test.exp AAA
equal

10.3 ファイルから文字列を読み込む方法

テスト用のファイルを作成します。

[root@server ~]# vi sample.txt
[root@server ~]# cat sample.txt
192.168.3.10 server
192.168.3.20 client

ファイルの終端(eof)まで1行ずつ変数fdに読み込み、それを標準出力に出力するスクリプトを作成します。

#!/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

10.4 文字列を分割する方法

splitコマンドに空白とハイフンをデリミッタにして文字列を分割するスクリプトを作成します。

[root@server ~]# vi test.exp
[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]

set src "333-444-555"
set dst [split $src "-"]
puts [lindex $dst 0]
puts [lindex $dst 1]
puts [lindex $dst 2]

スクリプトを実行します。

[root@server ~]# ./test.exp
000
111
222
333
444
555

11 日時を表示する方法

様々なフォーマットで時刻を表示するスクリプトを作成します。

[root@server ~]# vi test.exp
[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 ~]# ./test.exp
Year with 4 digits is 2023
Month number(01-12) is 04
Day of month is 05
Hour(00-23) is 20
Minutes(00-59) is 13
Seconds(00-59) is 10
Now is 2023/04/05,20:13:10

12 外部コマンドを実行する方法(exec)

execは外部コマンドの実行結果を返すコマンドです。たとえば、ls等のOSのコマンドを実行して、ファイル一覧を返すことができます。

12.1 時刻を表示する方法

setコマンドでdateの実行結果を変数xに設定して、putsコマンドで変数xの値を出力するスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

set x [exec date]
puts $x

スクリプトを実行すると、dateの実行結果が表示されていることがわかります。

[root@server ~]# ./test.exp
2023年  4月  5日 水曜日 20:17:44 JST

12.2 ファイルを表示する方法

setコマンドでls -lの実行結果を変数xに設定して、putsコマンドで変数xの値を出力するスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

set x [exec ls -l]
puts $x

スクリプトを実行すると、ファイルの一覧が表示されていることがわかります。

[root@server ~]# ./test.exp
合計 1564
-rw-r--r--. 1 tcpdump tcpdump     252  3月 30 21:49 1.cap
-rw-r--r--. 1 tcpdump tcpdump     312  3月 30 22:01 2.cap
-rw-r--r--. 1 tcpdump tcpdump     140  3月 30 22:03 3.cap
-snip-

12.3 カレンダーを表示する方法

カレンダーを表示するスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

set x [exec cal]
puts $x

スクリプトを実行すると、カレンダーが表示されていることがわかります。

[root@server ~]# ./test.exp
      4月 2023
日 月 火 水 木 金 土
                   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

13 配列の使い方

13.1 配列に値を代入/配列要素の値を表示する方法

setコマンドでxという名前の配列にaaa,bbb,cccを代入し、putsコマンドで配列の要素を表示するスクリプトを作成します。

[root@server ~]# vi test.exp
[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)

スクリプトを実行すると、配列xに代入した文字列が出力されていることがわかります。

[root@server ~]# ./test.exp
aaa
bbb
ccc

13.2 配列の要素数を求める方法(array size)

配列の要素数を求めるスクリプトを作成します。

[root@server ~]# vi test.exp
[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"

スクリプトを実行すると、配列の要素数が3であることがわかります。

[root@server ~]# ./test.exp
array has 3 elements

13.3 配列が存在するかどうかを確認する方法(array exists)

配列が存在するかどうかを確認するスクリプトを作成します。配列が存在するかどうかはarrayコマンドのexistsオプションを使って確認します。配列が存在する場合は1,配列が存在しない場合は0を返します。

[root@server ~]# vi test.exp
[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

13.4 指定した添字の要素を返す方法(array get)

arrayコマンドのgetオプションを使って、配列の要素を返すスクリプトを作成します。

[root@server ~]# vi test.exp
[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

13.5 2次元配列の使い方

スクリプトを作成します。

[root@server ~]# vi test.exp
[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

13.6 連想配列の使い方

連想配列とは、文字列など非負整数以外のデータを添字に使用することができる配列のことです。

tokyo,osakaを添え字にした連想配列に文字列の代入、代入した文字列を表示するスクリプトを作成します。

[root@server ~]# vi test.exp
[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

14 リストの使い方

リストとは、中括弧あるいはダブルクォートを使ってスペースで区切られた文字列のことです。

setコマンドを使って変数xにリストを代入して、putsコマンドで変数xに設定されたリストの第1、第3要素を標準出力に出力します。そして、最後にリストの要素数を出力するスクリプトを作成します。

[root@server ~]# vi test.exp
[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]"

スクリプトを実行すると、リストの第1、第3要素が表示されていることがわかります。また要素数が3であることもわかります。

[root@server ~]# ./test.exp
10
30
The length of list x is 3

15 ファイル操作

15.1 ファイルが存在するかどうかを確認する方法(file exists)

テスト用のファイルを作成します。

[root@server ~]# touch sample.txt
[root@server ~]# ls sample.txt
sample.txt

fileコマンドのexistsオプションを使って、カレントディレクトリにsample.txtが存在するかどうかを確認するスクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/tclsh

if {[file exists sample.txt] == 1} then {
        puts "FOUND"
} else {
        puts "NOT FOUND"
}

スクリプトを実行すると、カレントディレクトリにsample.txtが存在するので、FOUNDと表示されていることがわかります。

[root@server ~]# ./test.exp
FOUND

カレントディレクトリからテスト用ファイルを削除します。

[root@server ~]# rm sample.txt
rm: 通常ファイル 'sample.txt' を削除しますか? y

再度、スクリプトを実行します。カレントディレクトリにsample.txtが存在しないので、NOT FOUNDと表示されていることがわかります。

[root@server ~]# ./test.exp
NOT FOUND

15.2 ファイル名のドットまでの名前を返す方法(file rootname)

テスト用のファイルを作成します。

[root@server ~]# touch sample.tar.gz
[root@server ~]# ls sample.tar.gz
sample.tar.gz

fileコマンドのrootnameオプションを使って、最後のドットまでのファイル名を返すスクリプトを作成します。

[root@server ~]# vi test.exp
[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

15.3 ディレクトリを作成する方法(file mkdir)

テスト用ディレクトリを確認します。testディレクトリが存在しないことを確認します。

[root@server ~]# ls -ld test
ls: 'test' にアクセスできません: そのようなファイルやディレクトリはありません

fileコマンドのmkdirオプションを使って、ディレクトリを作成するスクリプトを作成します。

[root@server ~]# vi test.exp
[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  4月  5 22:01 test

16 変数の有効範囲

globalコマンドを使うことで、関数外の変数にアクセスすることができます。globalコマンドは、関数内で実行する必要があります。ここでは、関数test1内で変数xをグローバル変数として定義しています。

[root@server ~]# vi test.exp
[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 ~]#
[root@server ~]# ./test.exp
The return value of test1 is 10
The return value of test2 is abc

Y その他(sourceコマンドの使い方)

sourceコマンドは、外部のtclソースファイルを読み込むコマンドです。
source manual page - Built-In Commands

expectパッケージをインストールします。

[root@server ~]# dnf -y install expect

sourceコマンドで読み込むスクリプトを作成します。

[root@server ~]# vi proc.exp
[root@server ~]# cat proc.exp
proc test {} {
  puts "This is sample"
}

スクリプトを作成します。

[root@server ~]# vi test.exp
[root@server ~]# cat test.exp
#!/usr/bin/expect
source proc.exp
test

スクリプトを実行します。

[root@server ~]# ./test.exp
This is sample

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

Tcl