LoginSignup
5
5

More than 1 year has passed since last update.

Tclの使い方

Last updated at Posted at 2020-07-17

最新版は以下に記載しています。
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上の仮想マシンを使いました。
仮想マシンの版数は以下のとりです。

CentOSの版数
[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
カーネル版数
[root@server ~]# uname -r
3.10.0-957.el7.x86_64
tclの版数
[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

Tcl – 変数の有効範囲 -global, upvar-
send special characters

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5