Search code examples
tcltclsh

How to limit standard output at tclsh command line?


When running commands interactively at the tclsh command line, is there a way to truncate how much of a return value gets printed to stdout?

For example, this will take a very long time because the return value will print to stdout.

tclsh> set a [lrepeat 500000000 x]

I know I can add a dummy command in the same line, but this is an ad hoc solution. Is there some I could set in my ~/.tclshrc to truncate stdout to a finite length?

tclsh> set a [lrepeat 500000000 x] ; puts ""

Solution

  • Maybe this is an XY-problem (as turning off or swallowing prints to stdout seems to satisfy the OP), but the actual question was:

    Is there some I could set in my ~/.tclshrc to truncate stdout to a finite length?

    You can use an interceptor on stdout (and/ or, stderr) to cap strings to a default limit:

    oo::class create capped {
      variable max
      constructor {m} {
        set max $m
      } 
      method initialize {handle mode} {
        if {$mode ne "write"} {error "can't handle reading"}
        return {finalize initialize write}
      }
      method finalize {handle} {
        # NOOP
      }
      
      method write {handle bytes} {
        if {[string length $bytes] > $max} {
          set enc [encoding system]
          set str [encoding convertfrom $enc $bytes]
          set newStr [string range $str 0 $max-1]
          if {[string index $str end] eq "\n"} {
            append newStr "\n"
          }
          set bytes [encoding convertto $enc $newStr]
        }
        return $bytes
      }
    }
    

    Using chan push and chan pop you may turn on/off capping to, e.g., 30 characters:

    % chan push stdout [capped new 30]
    serial1
    % puts [string repeat € 35]
    €€€€€€€€€€€€€€€€€€€€€€€€€€€€€€
    % chan pop stdout
    % puts [string repeat € 35]
    €€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€
    

    Some remarks:

    • You can use an object, a namespace, or a proc offering the required interface of channel interceptors (initialize, write, ...); I prefer objects.

    • Ad write: You want to cap based on a character-based limit, not a byte-level one. However, write receives a string of bytes, not a string of characters. So, you need to be careful when enforcing the limit (back-transform the byte string into a char string, and vice versa, using encoding convertfrom and encoding convertto).

    • Similar, whether certain values of max might not be a good choice or the value range should be restricted. E.g., a max of 1 or 0 will turn off the basic REPL (the prompt % ), effectively.

    • As for tclshrc: You may want place the interceptor definition and chan push call therein, to enable capping per default?