How to limit standard output at tclsh command line?

149 views Asked by At

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 ""
2

There are 2 answers

1
mrcalvin On BEST ANSWER

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?

2
glenn jackman On

tclsh is a REPL, and the "P" there is what you're seeing. Without digging into the source, I don't know that there's a simple way to accomplish exactly what you're asking.

If I remember to do it, the list command is useful to provide no output

set a [lrepeat 500000000 x]; list

or perhaps something informative

set a [lrepeat 500000000 x]; llength $a

If you want to get programmy:

proc i {val} {set ::tcl_interactive $val}

Then do i off or i 0 or i false to turn off interactivity and then execute the commands with large results. Going non-interactive silences the printing of command results, but it also turns off the prompt which could be confusing. Restore interactivity with i on or i 1 or i true