tcl 引数処理

# まえがき
起動スクリプトから起動先のtclスクリプトに引数を渡す際のメモ。

# スクリプト

## パタン１

起動元スクリプトで引数のダブルクヲートつけてみる。

```bash
$cat caller.sh
#!/bin/bash

file="$1"
shift
args="$@"
exec tclsh "${file}" "$0" "${args}"
```

```tcl
$cat callee.tcl
#!/usr/bin/tclsh

puts $argv0
puts $argc
puts $argv
```

実行結果

グルーピングされている。

```bash
$./caller.sh callee.tcl {a..d}
callee.tcl
2
./caller.sh {a b c d}
```

引数の順番を入れ替えてみる。

```bash
$cat caller.sh
#!/bin/bash

file="$1"
shift
args="$@"
exec tclsh "${file}" "${args}" "$0"
```
```tcl
$cat callee.tcl
#!/usr/bin/tclsh

puts $argv0
puts $argc
puts $argv
```

実行結果

順序が入れ替わってグルーピングされている。

```bash
$./caller.sh callee.tcl {a..d}
callee.tcl
2
{a b c d} ./caller.sh
```

## パタン２

起動元スクリプトで引数のダブルクヲート外してみる。

```bash
$cat caller.sh
#!/bin/bash

file="$1"
shift
args="$@"
exec tclsh "${file}" "$0" ${args}
```

```bash
$cat callee.tcl
#!/usr/bin/tclsh

puts $argv0
puts $argc
puts $argv
```

実行結果

グルーピングされていない。すべて単一要素にフラット化されている。

```bash
$./caller.sh callee.tcl {a..d}
callee.tcl
5
./caller.sh a b c d
```

引数の順番を入れ替えてみる。

```bash
$cat caller.sh
#!/bin/bash

file="$1"
shift
args="$@"
exec tclsh "${file}" ${args} "$0"
```

```bash
$./caller.sh callee.tcl {a..d}
callee.tcl
5
a b c d ./caller.sh
```

実行結果

順序が入れ替わってもグルーピングされていない。すべて単一要素にフラット化されている。

```bash
$./caller.sh callee.tcl {a..d}
callee.tcl
5
a b c d ./caller.sh
```

# あとがき

挙動面白いなー。

以上、ありがとうございました。


# 20191112追記

[open](https://code-examples.net/ja/docs/tcl_tk/tclcmd/open.htm)
[文字列操作](https://freesoftnet.co.jp/webfiles/tclkits/doc/tclcom.html)

標準入力からの読み込み。パイプ経由からの場合とそれ以外の場合で書いてみた。
コマンドラインからの入力はコマンド置換結果をダブルクォートで囲んであげないと、意図した動きにならない。
もっといい書き方があれば知りたい。

```tcl
$cat 標準入力.tcl 
#!/usr/bin/tclsh

proc usage {} {
  puts "
Usage:
\$[info script] \"\$(echo {a..g} | xargs -n3)\"
{{a b c} {d e f} g}

コマンドライン経由引数

\$echo {a..g} | xargs -n3 | [info script] 
{{a b c} {d e f} g}

パイプ経由引数

\$echo {1..100} | xargs -n15 | [info script]
{{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 32 33 34 35 36 37 38 39 40 41 42 43 44 45} {46 47 48 49 50 51 52 53 54 55 56 57 58 59 60} {61 62 63 64 65 66 67 68 69 70 71 72 73 74 75} {76 77 78 79 80 81 82 83 84 85 86 87 88 89 90} {91 92 93 94 95 96 97 98 99 100}}

パイプ経由引数

  "
  exit 1
}

proc main {argc argv} {
  if {$argc!=0} {
    set input $argv
    if {[string length $input]==0} {
      usage
    }
    set input_done "[split [lindex $input 0] "\n"] {}"
    set input_done_done [lrange $input_done 0 end-1]
    set init_args "\{$input_done_done\}"
    puts $init_args
    puts ""
    puts "コマンドライン経由引数"
  } else {
    set stdin [open "| cat -"]
    set input [read $stdin]
    if {[string length $input]==1} {
      usage
    }
    set input_done "[split $input "\n"]"
    set input_done_done [lrange $input_done 0 end-1]
    set init_args "\{$input_done_done\}"
    puts $init_args
    puts ""
    puts "パイプ経由引数"
  }
}

main $argc $argv
```

実行例

```bash
$echo {1..30} | xargs -n15 | ./標準入力.tcl 
{{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}}

パイプ経由引数
$echo {1..20} | xargs -n15 | ./標準入力.tcl 
{{1 2 3 4 5 6 7 8 9 10 11 12 13 14 15} {16 17 18 19 20}}

パイプ経由引数
$./標準入力.tcl "$(echo {a..z})"
{{a b c d e f g h i j k l m n o p q r s t u v w x y z}}

コマンドライン経由引数
$./標準入力.tcl "$(echo {a..z}|xargs -n12)"
{{a b c d e f g h i j k l} {m n o p q r s t u v w x} {y z}}

コマンドライン経由引数
$echo {a..z} | xargs -n1 | ./標準入力.tcl 
{a b c d e f g h i j k l m n o p q r s t u v w x y z}

パイプ経由引数
$echo {a..z} | xargs -n25 | ./標準入力.tcl 
{{a b c d e f g h i j k l m n o p q r s t u v w x y} z}

パイプ経由引数
$echo {a..z} | xargs -n26 | ./標準入力.tcl 
{{a b c d e f g h i j k l m n o p q r s t u v w x y z}}

パイプ経由引数
$echo {a..a} | xargs -n1 | ./標準入力.tcl 
{a}

パイプ経由引数
```

コマンド作成した際、そのテストデータ作成時に便利かなって思っている。

ただ、単一と複数の住み分けがうまくいけてない。課題。

剰余グルーピングして単一要素の際はカーリブランケット**{}**は外されている。index参照はきちんと効く！。

```bash
$tclsh
% set a {{a b c} {d e f} {g h i} j}
{a b c} {d e f} {g h i} j
% puts $a
{a b c} {d e f} {g h i} j
% puts [lindex $a 0 1]
b
% puts [lindex $a 3]      
j
% puts [lindex $a 3 0]
j
```

単一か複数の問題はコマンドライン経由の引数を文字列長さ判定値を**1**から**0**に変更した。

```bash
#単一文字
$./標準入力.tcl "$(echo a)"
{a}

コマンドライン経由引数
#単一文字範囲指定
$./標準入力.tcl "$(echo {a..a})"
{a}

コマンドライン経由引数
#単一空文字
$./標準入力.tcl ""
{}

コマンドライン経由引数
#複数空文字
$./標準入力.tcl "" ""
{}

コマンドライン経由引数
#未指定（キー入力待ち。Ctrl+Cでquit）
$./標準入力.tcl 
^C
#パイプ経由の単一改行
$echo | ./標準入力.tcl

Usage:
#パイプ経由の単一文字
$echo a | ./標準入力.tcl
{a}

パイプ経由引数
#パイプ経由の単一空文字
$echo "" | ./標準入力.tcl

Usage:
  
#パイプ経由の単一文字
$echo "a" | ./標準入力.tcl
{a}

パイプ経由引数
```