2.3. Pattern Matching

With what we know so far, we need to write a separate extension for each telephone number. As the system expands, this leads to unwieldy and error-prone dialplans. Say that, for our example, we need numbers 100 to 109 to play the "hello world" sound file. Our extensions.conf would look like this:
[general]

[widgets]
exten => 100,1,Answer()
exten => 100,2,Playback(hello-world)
exten => 100,3,Hangup()

exten => 101,1,Answer()
exten => 101,2,Playback(hello-world)
exten => 101,3,Hangup()

exten => 102,1,Answer()
exten => 102,2,Playback(hello-world)
exten => 102,3,Hangup()

exten => 103,1,Answer()
exten => 103,2,Playback(hello-world)
exten => 103,3,Hangup()

exten => 104,1,Answer()
exten => 104,2,Playback(hello-world)
exten => 104,3,Hangup()

exten => 105,1,Answer()
exten => 105,2,Playback(hello-world)
exten => 105,3,Hangup()

exten => 106,1,Answer()
exten => 106,2,Playback(hello-world)
exten => 106,3,Hangup()

exten => 107,1,Answer()
exten => 107,2,Playback(hello-world)
exten => 107,3,Hangup()

exten => 108,1,Answer()
exten => 108,2,Playback(hello-world)
exten => 108,3,Hangup()

exten => 109,1,Answer()
exten => 109,2,Playback(hello-world)
exten => 109,3,Hangup()
If we use a pattern, the same dialplan becomes instantly more compact and elegant:
[general]

[widgets]
exten => _10X,1,Answer()
exten => _10X,2,Playback(hello-world)
exten => _10X,3,Hangup()
The '_10X' extension describes the number range from 100 to 109.

Note

The terms pattern and regular expression are often casually interchanged; in general, what we are using in Asterisk is a pattern, though many programmers would use the term regular expression also.

Syntax

Dialplan patterns always begin with the underscore (_) character:
exten => _Pattern,Priority,Applikation
An Asterisk dialplan pattern can have the following elements:
[abc]
The digits a, b and c. For example, to match 34, 37, and 38:
exten => _3[478],1,NoOp(Test)
[a-b]
Any digit in the range a to b. For example, to match any number between 31 and 35:
exten => _3[1-5],1,NoOp(Test)
(e.g. [25-8] is also acceptable for the digits 2,5,6,7,8)
X
Any digit from 0 to 9. For example, to match any number between 300 and 399:
exten => _3XX,1,NoOp(Test)
Z
Any digit from 1 to 9. For example, to match any number between 31 and 39:
exten => _3Z,1,NoOp(Test)
N
Any digit from 2 to 9. For example, to match any number between 32 and 39:
exten => _3N,1,NoOp(Test)
.
Any number of digits of any kind. For example, to match all numbers that begin with 011:
exten => _011.,1,NoOp(Test)

Warning

Don't use the '_.' pattern! This will also include special extensions such as i, t and h. Use _X. or _X if you need broad pattern matching.
!
This special 'wildcard' character will match as soon as the number dialled is unambiguous; i.e. when the number being dialled cannot match any other extension in the context. Once a match is made, the outgoing line is picked up and dialing proceeds in real-time with direct feedback (this is known as 'overlap dialing').

Important

A common error is to forget the underscore ("_") character at the beginning of the pattern. This convention is necessary because SIP devices, as configured in sip.conf, can have alphanumeric names (For example, in Asterisk, '333', 'loadingdock' and 'A31' are all acceptable names for a SIP device). It also means that if you forget to use the underscore, your extension will never match and you will never see an error message informing you of your mistake.

Testing a pattern using dialplan show

An example dialplan looks like this:
[general]

[my-phones]
exten => 23,1,Answer()
exten => 23,2,Playback(hello-world)
exten => 23,3,Hangup()
We can call dialplan show from the CLI (invoked with asterisk -r if Asterisk is already running) to verify that our dialplan has been loaded:
*CLI> dialplan show
[ Context 'default' created by 'pbx_config' ]

[ Context 'my-phones' created by 'pbx_config' ]
  '23' =>           1. Answer()                                   [pbx_config]
                    2. Playback(hello-world)                      [pbx_config]
                    3. Hangup()                                   [pbx_config]

[ Context 'parkedcalls' created by 'res_features' ]
  '700' =>          1. Park()                                     [res_features]

-= 2 extensions (4 priorities) in 3 contexts. =-
*CLI>
The output includes all the dialplan rules that Asterisk knows about. Notice that there is a 'parkedcalls' context that we haven't seen before; this is activated by default in features.conf and needn't concern us further. What if we are only interested in the my-phones context? We can make our request more specific with dialplan show my-phones:
*CLI> dialplan show my-phones
[ Context 'my-phones' created by 'pbx_config' ]
  '23' =>           1. Answer()                                   [pbx_config]
                    2. Playback(hello-world)                      [pbx_config]
                    3. Hangup()                                   [pbx_config]

-= 1 extension (3 priorities) in 1 context. =-
*CLI> 
The command dialplan show can also be used to show what Asterisk will do if we dial a specific number. Say we want to dial '25' from a phone in the my-phones context. We can see what will happen with the command dialplan show 25@my-phones:
*CLI> dialplan show 25@my-phones
There is no existence of 25@my-phones extension
*CLI>
Nothing happens because there is no match for '25' in the context. If we dial '23' instead, we get this output:
*CLI> dialplan show 23@my-phones
[ Context 'my-phones' created by 'pbx_config' ]
  '23' =>           1. Answer()                                   [pbx_config]
                    2. Playback(hello-world)                      [pbx_config]
                    3. Hangup()                                   [pbx_config]

-= 1 extension (3 priorities) in 1 context. =-
*CLI> 
If we want to check '23' against all the accessible contexts, we use dialplan show 23@:
*CLI> dialplan show 23@
[ Context 'my-phones' created by 'pbx_config' ]
  '23' =>           1. Answer()                                   [pbx_config]
                    2. Playback(hello-world)                      [pbx_config]
                    3. Hangup()                                   [pbx_config]

-= 1 extension (3 priorities) in 1 context. =-
*CLI>
Let's expand our dialplan with an additional context by editing extensions.conf like so:
[general]

[my-phones]
exten => 23,1,Answer()
exten => 23,2,Playback(hello-world)
exten => 23,3,Hangup()

[department-q]
exten => _2X,1,Answer()
exten => _2X,2,Playback(hello-world)
exten => _2X,3,Hangup()
Now we go back to the CLI and, after reloading the dialplan with the reload command, run dialplan show 23@:
*CLI> dialplan show 23@
[ Context 'department-q' created by 'pbx_config' ]
  '_2X' =>          1. Answer()                                   [pbx_config]
                    2. Playback(hello-world)                      [pbx_config]
                    3. Hangup()                                   [pbx_config]

[ Context 'my-phones' created by 'pbx_config' ]
  '23' =>           1. Answer()                                   [pbx_config]
                    2. Playback(hello-world)                      [pbx_config]
                    3. Hangup()                                   [pbx_config]

-= 2 extensions (6 priorities) in 2 contexts. =-
*CLI> 
All the matching extensions are displayed. Let's try it with dialplan show 25@:
*CLI> dialplan show 25@
[ Context 'department-q' created by 'pbx_config' ]
  '_2X' =>          1. Answer()                                   [pbx_config]
                    2. Playback(hello-world)                      [pbx_config]
                    3. Hangup()                                   [pbx_config]

-= 1 extension (3 priorities) in 1 context. =-
*CLI>
There is only one match, in context department-q. In this example, if you dial '25' from a phone in the my-phones context, you still won't hear the 'hello world' message. Extension '25' only works for phones in the department-q context.

Pattern matching order

Employing pattern matching in your Asterisk dialplan, while very powerful, can be tricky. It is easy to assume that Asterisk runs through the dialplan in a completely sequential manner; while this is generally the case, it does prioritize patterns based on the quality of the match.
The reason for this is simple: more than one pattern might match a dialled number. If two extensions match a dialled number, Asterisk will always choose the better match. Before deciding which extension matches best, it processes the entire context.
An example:
[sales]
exten => _12X.,1,NoOp{12X}
exten => 12345,1,NoOp(12345}
exten => _1234.,1,NoOp{1234.}
It is not immediately clear which extension is executed when we dial '12345'. To find out, we use dialplan show 12345@sales:
*CLI> dialplan show 12345@sales
[ Context 'sales' created by 'pbx_config' ]
  '12345' =>        1. NoOp(12345})                               [pbx_config]
  '_1234.' =>       1. NoOp{1234.}()                              [pbx_config]
  '_12X.' =>        1. NoOp{12X}()                                [pbx_config]

-= 3 extensions (3 priorities) in 1 context. =-
*CLI> 
Asterisk shows all the hits, but gives extension 12345,1,NoOP{12345} first priority. The highest priority extension is always displayed at the top.
Let's try it with '12346' using the command dialplan show 12346@sales:
*CLI> dialplan show 12346@sales
[ Context 'sales' created by 'pbx_config' ]
  '_1234.' =>       1. NoOp{1234.}()                              [pbx_config]
  '_12X.' =>        1. NoOp{12X}()                                [pbx_config]

-= 2 extensions (2 priorities) in 1 context. =-
*CLI> 
Again, the pattern with the best match to the dialled digits is listed first.

Important

The order in which the patterned extensions appear in the dialplan makes no difference. Patterned extensions are matched strictly in order of match precision.

A special case - the pattern "_." in Asterisk 1.2

To be sure that an Asterisk administrator's job doesn't become too easy, Digium has changed the expected behavior for the "_." pattern in Asterisk 1.2. Though the pattern is the most general and should be therefore assigned the lowest priority, the behavior is opposite the expected behavior.

Warning

In Asterisk 1.2, the extension "_." always gets the highest priority!

Note

Note that the show dialplan command will work in Asterisk 1.4 but is deprecated; henceforth, examples for Asterisk 1.2 use show dialplan, while dialplan show is used for examples in Asterisk 1.4.
Let's try adding the extension "_." to our previous dialplan example:
[sales]
exten => _12X.,1,NoOp{12X}
exten => 12345,1,NoOp(12345}
exten => _1234.,1,NoOp{1234.}

exten => _.,1,NoOp{Bingo}
When we try testing '12346' with dialplan show 12346@sales, we get the following output:
*CLI> dialplan show 12346@sales
[ Context 'sales' created by 'pbx_config' ]
  '_1234.' =>       1. NoOp{1234.}()                              [pbx_config]
  '_12X.' =>        1. NoOp{12X}()                                [pbx_config]
  '_.' =>           1. NoOp{Bingo}()                              [pbx_config]

-= 3 extensions (3 priorities) in 1 context. =-
*CLI>
In Asterisk 1.2, show dialplan 12346@sales gives a very different result:
*CLI> show dialplan 12346@sales
[ Context 'sales' created by 'pbx_config' ]
  '_.' =>           1. NoOp{Bingo}()                              [pbx_config]
  '_1234.' =>       1. NoOp{1234.}()                              [pbx_config]
  '_12X.' =>        1. NoOp{12X}()                                [pbx_config]

-= 3 extensions (3 priorities) in 1 context. =-
*CLI>
This is why it is preferable to use "_X." as the wildcard pattern (if we use a wildcard pattern at all!). The following dialplan example is processed identically in Asterisk 1.2 and 1.4:
[sales]
exten => _12X.,1,NoOp{12X}
exten => 12345,1,NoOp(12345}
exten => _1234.,1,NoOp{1234.}

exten => _X.,1,NoOp{Bingo}
The priorities appear as follows in both versions:
*CLI> dialplan show 12346@sales
[ Context 'sales' created by 'pbx_config' ]
  '_1234.' =>       1. NoOp{1234.}()                              [pbx_config]
  '_12X.' =>        1. NoOp{12X}()                                [pbx_config]
  '_X.' =>          1. NoOp{Bingo}()                              [pbx_config]

-= 3 extensions (3 priorities) in 1 context. =-
*CLI>