2

I want to execute these piped shell commands in Tcl:

grep -v "#" inputfile | grep -v ">" | sort -r -nk7 | head

I try:

exec grep -v "#" inputfile | grep -v ">" | sort -r -nk7 | head

and get an error:

Error: grep: invalid option -- 'k'

When I try to pipe only 2 of the commands:

exec grep -v "#" inputfile | grep -v ">" 

I get:

Error: can't specify ">" as last word in command

Update: I also tried {} and {bash -c '...'}:

exec {bash -c 'grep -v "#" inputfile | grep -v ">"'} 

Error: couldn't execute "bash -c 'grep -v "#" inputfile | grep -v ">"'": no such file or directory

My question: how can I execute the initial piped commands in a tcl script?

Thanks

2
  • @Etan Reisne I tried e.g.: > exec {grep -v "#" cg_metrics.rpt | grep -v ">"} but I got: Error: couldn't execute "grep -v "#" cg_metrics.rpt | grep -v ">"": no such file or directory Thanks Commented Apr 18, 2016 at 14:19
  • It still gives errors: >exec {bash -c 'grep -v "#" cg_metrics.rpt | grep -v ">"'} Error: couldn't execute "bash -c 'grep -v "#" cg_metrics.rpt | grep -v ">"'": no such file or directory Commented Apr 18, 2016 at 14:24

3 Answers 3

4

The problem is that exec does “special things” when it sees a > on its own (or at the start of a word) as that indicates a redirection. Unfortunately, there's no practical way to avoid this directly; this is an area where Tcl's syntax system doesn't help. You end up having to do something like this:

exec grep -v "#" inputfile | sh -c {exec grep -v ">"} | sort -r -nk7 | head

You can also move the entire pipeline to the Unix shell side:

exec sh -c {grep -v "#" inputfile | grep -v ">" | sort -r -nk7 | head}

Though to be frank this is something that you can do in pure Tcl, which will then make it portable to Windows too…

Sign up to request clarification or add additional context in comments.

1 Comment

In regular shell scripting I don't like sh -c because it puts shell code inside a string and loses its syntax highlighting. But tcl using curl braces is an elegant improvement over this so I'm really happy with this solution.
2

The > is causing problems here.

You need to escape it from tcl and the shell to make it work here.

exec grep -v "#" inputfile | grep -v {\\>} | sort -r -nk7 | head

or (and this is better since you have one less grep)

exec grep -Ev {#|>} inputfile | sort -r -nk7 | head    

If you look in the directory you were running this from (assuming tclsh or similar) you'll probably see that you created an oddly named file (i.e. |) before.

4 Comments

This doesn't give an error, but doesn't work I'm afraid. It does not escape the lines containing ´#´ or >. Indeed a file was created as you described. Thanks
You might need grep -E for that.
It works for me with grep -E what version of grep are you using?
I m using version 8.5
2

In pure Tcl:

package require fileutil

set lines {}
::fileutil::foreachLine line inputfile {
    if {![regexp #|> $line]} {
        lappend lines $line
    }
}
set lines [lsort -decreasing -integer -index 6 $lines]
set lines [lrange $lines 0 9]
puts [join $lines \n]\n

(-double might be more appropriate than -integer)

Edit: I mistranslated the (1-based) -k index for the command sort when writing the (0-based) -index option for lsort. It is now corrected.

Documentation: fileutil package, if, join, lappend, lrange, lsort, package, puts, regexp, set

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.