How can I updated this regex so that it matches even if bob
isn't present in the line? (Python).
^(AllowUsers.*) (\bbob\b) ?(.*)$
My naive thought was to just add a "0 or 1" quantifier on capture group 2: (\bbob\b)?
, but when I do that, lines that have bob
somewhere other than the end no longer match:
### without "?" on capture group 2
# Match
AllowUsers bob
AllowUsers bob billybob
AllowUsers billybob bob
AllowUsers billybob steve bob eric bobby
AllowUsers billybob bobby steve bob eric
AllowUsers richard bob
AllowUsers richard bob jeremy
AllowUsers bob james richard jeremy
AllowUsers bob jeremy
# no match
AllowUsers james richard jeremy
### With "?" on capture group 2:
# All lines match, but `bob` is not captured unless it's at the end of the line:
AllowUsers bob # `bob` captured
AllowUsers billybob bob # `bob` captured
AllowUsers bob billybob # `bob` not captured
My understanding of the regex (with ?
on group 2) is:
^(AllowUsers.*)
: Match lines that start withAllowUsers
and capture that any anything after (group 1), not including the space. This is greedy.(\bbob\b)?
: Match and capturebob
(group 2), if it exists. We use word boundaries (\b
) so that we don't incorrectly match, for example,billybob
.?(.*)$
: Match an optional space and capture anything thereafter (group 3).
Here's the regex101 link: https://regex101.com/r/39zNfm/1
If I remove the "0 or 1" quantifier on (\bbob\b)
, then I match all lines that have bob
in them and I get the correct capture groups, but I no longer match lines that don't have bob
in them.
What am I misunderstanding?
Desired match and capture behavior
- The regex should match any line that starts with
AllowUsers
, whether or notbob
is present in the line. - If
bob
is not in the line, then capture the entire line. Do so in two groups: group 1 and group 3. It's OK if group 3 is empty. - If
bob
is in the line, then capture everything before (group 1), including (group 2), and after (group 3)
For example:
Background
I'm writing an Ansible task using the lineinfile
builtin. The goal of this task is to add users to the AllowUsers
directive of /etc/ssh/sshd_config
.
With lineinfile
, the regex used must match the line before and after modification so that you maintain idempotence.
In the end, the task would look like:
- name: "Allow {{ user }} to log in via SSH"
lineinfile:
path: '/etc/ssh/sshd_config'
state: present
regexp: "^(AllowUsers.*) (\b{{ user }}\b)?(\w*)$" # not currently workng
line: "\1 {{ user }} \3" # might have to fiddle with literal space. eg: "\1{{ user}}\3"
backrefs: yes
loop: { ssh_users }
loop_control:
loop_var: user