2

I am using tcl to perform some pattern matching. The following is the string that I am performing the match on:

ps -ef | grep ipqosmgr
root     17255 17136  0 22:34 ttyS0    00:00:00 grep ipqosmgr
root     28986 17731  0 Jun05 ?        00:02:01 /isan/bin/ipqosmgr

Typically I would want the 3rd line

root     28986 17731  0 Jun05 ?        00:02:01 /isan/bin/ipqosmgr

since I would like the process ID associated with the process.

When I use the following regular expression, it works as expected:

% foreach line [split $output \n] {
    if { [ regexp -nocase {root\s+([0-9]+)\s+.*(/isan/bin/ipqosmgr)} $line - value ] } {
        puts $line
    }
}
root     28986 17731  0 Jun05 ?        00:02:01 /isan/bin/ipqosmgr
% puts $value
28986
% 

However, I would like this code to run for multiple processes and hence have put this in a function with $process which will hold the value of the process. When I use the same regex with the variable $process, it fails.

% puts $process
ipqosmgr
%
% foreach line [split $output \n] {
    if { [ regexp -nocase {root\s+([0-9]+)\s+.*(/isan/bin/$process)} $line - value ] } {
        puts $line
    }
}
% 
% puts $value
can't read "value": no such variable
% 

I have no idea as to why its behaving this way and it would be really great if someone could tell me whats going wrong here and how to rectify it.

2
  • 2
    Use "root\\s+(\[0-9\]+)\\s+.*(/isan/bin/$process)" if you plan to use string interpolation. Commented Jun 7, 2018 at 8:02
  • 1
    If you're familiar with shell script programming, Tcl braces are like shell single quotes: no variable interpolation is performed. Commented Jun 7, 2018 at 14:02

3 Answers 3

2

You can prepare the regexp with format as below:

foreach line [split $output \n] {
    set regex [format {root\s+([0-9]+)\s+.*(/isan/bin/%s)} $process]
    if { [ regexp -nocase $regex $line - value ] } {
        puts $line
    }
}

The problem with the way you were using the expression is that braces prevent variable substitution, and while you can use quotes instead for the regular expression, you will have to escape a lot of characters (square parens, backslashes for instance) and to avoid having to escape these, using format can be simpler to use.

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

Comments

1

You should use a double quoted string literal if you plan to use string iterpolation, and mind escaping [ and ] to prevent interpreting them as commands as well escaping each \ to define literal backslashes (e.g. with the shorthand character class \s here):

regexp -nocase "root\\s+(\[0-9\]+)\\s+.*(/isan/bin/$process)" $line - value

See the Tcl demo online.

Here,

  • root - a substring root
  • \\s+ - parsed as \s+ - 1 or more whitespace chars
  • (\[0-9\]+) - parsed as ([0-9]+) - Capturing group 1 - 1 or more digits
  • \\s+ - 1 or more whitespaces
  • .* - any 0+ chars
  • (/isan/bin/$process) - parsed as (/isan/bin/ipqosmgr) - Capturing group 1 matching a /isan/bin/ipqosmgr substring (or any /isan/bin/ + $process).

Comments

1

Use variable substitution with the subst command. Also if any of the -nobackslashes, -nocommands, or -novariables are specified, then the corresponding substitutions are not performed. For example, if -nocommands is specified, command substitution is not performed: open and close brackets are treated as ordinary characters with no special interpretation.

% set output "
root     17255 17136  0 22:34 ttyS0    00:00:00 grep ipqosmgr
root     28986 17731  0 Jun05 ?        00:02:01 /isan/bin/ipqosmgr
"

and with variable set

% set process "ipqosmgr"

You could very well do,

% foreach line [split $output \n] {
    if { [ regexp -nocase [subst -nocommands -nobackslashes {root\s+([0-9]+)\s+.*(/isan/bin/$process)}] $line - value ] } {
        puts $line
    }
}
root     28986 17731  0 Jun05 ?        00:02:01 /isan/bin/ipqosmgr

does the match as expected

% puts $value
28986

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.