Skip to content
VLSI Mentor

Tcl Part I · Module 13 · Regular Expressions

The Useful Switches: -all, -inline, -line, -nocase

So far regexp has found one match and reported it through variables. Four switches change that in ways you will want often: one finds every match rather than stopping at the first, one hands results back as a list instead of assigning them, one makes the anchors mean line boundaries so a multi-line string can be searched line by line, and one ignores case. They combine, and the combination of the first two has a result shape that catches people out the first time — so this chapter spends real time on exactly what comes back.

Intermediate11 min readTclPart IRegexSwitchesParsing

Part I · Module 13 · Chapter 13.6 · The Useful Switches: -all, -inline, -line, -nocase

1. -all counts; it does not collect

The first surprise. -all finds every match, and on its own it returns how many:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set text "alpha 1.5 beta 2.25 gamma 30"

puts [regexp -all -- {\d+\.\d+} $text]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2

Two matches — and no way to see what they were. That is useful for counting and almost never what you wanted.

2. -inline returns the matches

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts [regexp -all -inline -- {\d+\.\d+} $text]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1.5 2.25

-inline says "return the result instead of assigning it to variables". With -all you get every match as a list, which is the form you will use most.

Without -all, -inline returns just the first match — as a list containing the whole match followed by each group:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts [regexp -inline {(\w+) (\S+)} $text]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
{alpha 1.5} alpha 1.5

Three elements: the whole match alpha 1.5, then group 1 alpha, then group 2 1.5. That is the same information regexp ... -> a b would have put in variables, handed back as a value instead.

3. Patterns starting with a dash

Chapter 13.2 §6 showed that a pattern beginning with - is read as a switch. With switches present it is easy to forget:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
regexp -all -inline {-?\d+\.\d+} $text
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
bad switch "-?\d+\.\d+": must be -all, -about, -indices, -inline, -expanded, -line, -linestop, -lineanchor, -nocase, -start, or --
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts [regexp -all -inline -- {-?\d+\.\d+} "a 1.5 b -2.25"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1.5 -2.25

Write -- before the pattern whenever you use switches. It costs nothing when unnecessary and prevents this entirely.

4. -all -inline with capture groups

Here is the result shape to understand before you rely on it. With groups, each match contributes the whole match and its groups, all flattened into one list:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set log "INFO ok\nERROR bad thing\nINFO fine\nERROR worse"

set found [regexp -line -all -inline {^ERROR (.*)$} $log]
puts "list     : $found"
puts "elements : [llength $found]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
list     : {ERROR bad thing} {bad thing} {ERROR worse} worse
elements : 4

Two matches, two elements each: the whole line, then the captured message. They are not grouped per match — it is one flat list.

So walk it with foreach taking as many variables per pass as the pattern has groups plus one — Chapter 9.3's grouping:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
foreach {whole message} $found {
    puts "  message: '$message'"
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  message: 'bad thing'
  message: 'worse'

5. -line makes the anchors mean line boundaries

By default ^ and $ mean the start and end of the whole string, even when it contains newlines:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set log "INFO ok\nERROR bad thing\nINFO fine\nERROR worse"

puts "without -line : '[regexp -all -inline {^ERROR (.*)$} $log]'"
puts "with -line    : '[regexp -line -all -inline {^ERROR (.*)$} $log]'"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
without -line : ''
with -line    : '{ERROR bad thing} {bad thing} {ERROR worse} worse'

Without -line the pattern asks for a string that begins with ERROR and ends after the message — and this string begins with INFO, so there is no match at all. With -line, each line is a candidate.

This is what you want whenever you have read a file with read (Chapter 12.3) and want to search it line by line without splitting it first.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
regexp -line {^ERROR (.*)$} $log -> first
puts "first error : $first"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
first error : bad thing

6. -nocase

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set text "alpha 1.5 beta 2.25 gamma 30"

puts "plain    : [regexp {ALPHA} $text]"
puts "-nocase  : [regexp -nocase {ALPHA} $text]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
plain    : 0
-nocase  : 1

It applies to the whole pattern. For case-insensitivity in one part only, write the alternatives into the pattern — [Ee]rror — which is clearer than making the entire thing insensitive.

7. -indices, for positions rather than text

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
regexp -indices {\d+} "ab123cd" where
puts "match occupies characters $where"

lassign $where start end
puts "which is '[string range "ab123cd" $start $end]'"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
match occupies characters 2 4
which is '123'

-indices replaces each captured string with a two-element list of start and end offsets. It is what you need when you intend to modify the original rather than just read from it — though for most rewriting, Chapter 13.7 is the better tool.

8. A reference

SwitchEffect
-allfind every match; on its own, returns the count
-inlinereturn results instead of assigning to variables
-all -inlineevery match (and its groups) as one flat list
-line^ and $ match at line boundaries
-nocaseignore case for the whole pattern
-indicesreport offsets instead of text
--end the switches; always use it before the pattern

9. A worked example

Extracting every timestamped error from a log held as one string:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set log "2026-01-05 10:00:01 INFO  starting up
2026-01-05 10:00:04 ERROR cannot open input.csv
2026-01-05 10:00:04 WARN  retrying
2026-01-05 10:00:09 ERROR giving up after 2 attempts
2026-01-05 10:00:09 INFO  finished"

set pattern {^(\S+ \S+) ERROR (.*)$}
set hits [regexp -line -all -inline -- $pattern $log]

puts "[expr {[llength $hits] / 3}] error(s):"
foreach {whole when message} $hits {
    puts [format "  %s  %s" $when $message]
}

set counts [dict create]
set levels [regexp -line -all -inline -- {^\S+ \S+ (INFO|WARN|ERROR)} $log]
foreach {whole lvl} $levels {
    dict incr counts $lvl
}
puts "by level : $counts"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 error(s):
  2026-01-05 10:00:04  cannot open input.csv
  2026-01-05 10:00:09  giving up after 2 attempts
by level : INFO 2 ERROR 2 WARN 1

The / 3 is the stride from section 4: one pattern with two groups gives three elements per match. The second pass counts levels with a one-group pattern, so its stride is two.

10. Exercises

Exercise 1. What does regexp -all -- {\d+} $text return?

Exercise 2. What does adding -inline change?

Exercise 3. For a pattern with two capture groups and three matches, how many elements does -all -inline return?

Exercise 4. Why does regexp -all -inline {^ERROR (.*)$} $multiline return nothing without -line?

Exercise 5. Why should -- appear before the pattern whenever switches are used?

Exercise 6. Write a one-liner returning every integer in $text as a list.

Answers

1. The number of matches — a count, not the matches themselves.

2. It returns the matches as a list rather than assigning anything to variables. With -all you get every match; without it, the first match plus its groups.

3. Nine — three elements per match (the whole match plus two groups), times three matches, flattened into a single list.

4. Because without -line the anchors mean the start and end of the entire string. The string begins with something other than ERROR, so the pattern cannot match anywhere. -line makes ^ and $ apply at each line boundary.

5. Because a pattern beginning with - is otherwise read as another switch, giving bad switch "...": must be -all, -about, .... -- ends the switch list and costs nothing when it was not needed.

6.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set text "a 12 b 345 c 6"
puts [regexp -all -inline -- {\d+} $text]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
12 345 6

11. Remember

  • -all on its own returns a count, not the matches.
  • -inline returns results instead of assigning them, so they compose with every list command.
  • -all -inline gives one flat list — the whole match plus each group, for every match.
  • Count the groups to get the foreach stride right: groups plus one element per match.
  • -line makes ^ and $ match at line boundaries; without it they mean the ends of the whole string.
  • -nocase applies to the entire pattern — use [Ee] for one part only.
  • -indices reports offsets rather than text.
  • Write -- before the pattern whenever you use switches.

Where this fits

Part of the Tcl curriculum.