Question

I have searched the web (including StackOverflow) for an hour now and can't seem to figure this simple thing out. I'm writing an expect script for some automation solution, and it's something quite small (automating the creation of encrypted Truecrypt volumes, feeding entropy data from /dev/urandom into Truecrypt using expect/Tcl, because this only works interactively).

My main problem is: How do I run the Tcl exec command to launch a system command including square brackets, and then feed the output to a variable? Like this:

set entropyfeed [exec tr -dc [:graph:] < /dev/urandom | head -c 320]

So this is supposed to read pseudorandom data from /dev/urandom, then filter out all the non-printable characters ("[:graph:]" only!) using tr and return the first 320 printable chars into the variable $entropyfeed. This works with any other command that does not use arguments with square brackets!

Tcl seems to always interpret [] as a kind of backticks, trying to run what's inside as a Tcl command. I was unable to find a way to escape the brackets in that exec call properly and have this run like I want, no matter whether I tried \[:graph:\] or {[:graph:]} or multiple backslashes or whatever. I'd really like to use [:graph:] instead of some pattern like a-zA-Z0-9. Plus, I now really wanna now how to do this right!

set entropyfeed [exec tr -dc \[:graph:\] < /dev/urandom | head -c 320]

This isn't right...

set entropyfeed [exec tr -dc {[:graph:]} < /dev/urandom | head -c 320]

...and this isn't either.

set entropyfeed [exec tr -dc \\[:graph:\\] < /dev/urandom | head -c 320]

Nope.

set entropyfeed [exec tr -dc \\\[:graph:\\\] < /dev/urandom | head -c 320]

Na-ah. So just desperately drowning that line in backslashes isn't going to help...

In the {[:graph:]} and \\[:graph:\\] cases it would actually print the characters I need on screen (almost a success?) but then die with the error "child killed: write on pipe with no readers". This doesn't happen when I run the original command on a Bash. It seems as if the data's been misdirected somehow in this case, but I'm just at a loss here.

Any insights into how this is done properly in Tcl/expect are much appreciated, I have zero experience in this language!

Was it helpful?

Solution

Yeah, that's annoying. Since a shell doesn't seem bothered by it, you could do this:

set entropyfeed [exec sh -c {tr -dc '[:graph:]' < /dev/urandom | head -c 320}]

Or if you just want a random string of printable ascii chars:

proc random_string {n} {
    for {set i 1} {$i <= $n} {incr i} {
        append data [format %c [expr {33+int(94*rand())}]]
    }
    return $data
}
set entropyfeed [random_string 320]

OTHER TIPS

This would be the right one:

set entropyfeed [exec tr -dc {[:graph:]} < /dev/urandom | head -c 320]

Except that the early close of the output pipe (because that's how head works) causes tr to exit due to a signal, and exec picks that up and generates an error. The exec command is sometimes very annoying.

It might be easiest to just use Tcl to do all the work directly:

set entropyfeed ""
set f [open /dev/urandom rb]
# Older versions of Tcl need:
#    set f [open /dev/urandom]
#    fconfigure $f -translation binary
while {[string length $entropyfeed] < 320} {
    set c [read $f 1]
    if {[string is graph $c]} {
        append entropyfeed $c
    }
}
close $f

That should be pretty much identical, except now it only relies on the system device and not external programs. (You could use Tcl's built-in RNG instead, but we don't make any kind of guarantees on how good that is. Other than that it's thread-local — or rather it's interpreter-local — so there's no nasty surprises in there.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top