I'm trying to write a commit-msg hook for my git project that checks if commit message accord with a particular style guide. However, it looks like something works differently into the bash world regarding regexp. How could I accomplish my goal?

#!/usr/bin/env bash

read -r -d '' pattern << EOM
(?x)                                      # Enable comments and whitespace insensitivity.
^                                         # Starting from the very beginning of the message
(feat|fix|docs|style|refactor|test|chore) # should be a type which might be one of the listed,
:[ ]                                      # the type should be followed by a colon and whitespace,
(.{1,50})\n                               # then goes a subject that is allowed to be 50 chars long at most,
(?:\n((?:.{0,80}\n)+))?                   # then after an empty line goes optional body each line of which
                                          # may not exceed 80 characters length.
$                                         # The body is considered everything until the end of the message.

message=$(cat "${1}")

if [[ ${message} =~ ${pattern} ]]; then
  exit 0
  error=$(tput setaf 1)
  normal=$(tput sgr0)
  echo "${error}The commit message does not accord with the style guide that is used on the project.${normal}"
  echo "For more details see https://udacity.github.io/git-styleguide/"
  exit 1

I also tried writing this regexp in oneline, like as:

pattern="^(feat|fix|docs|style|refactor|test|chore):[ ](.{1,50})\n(?:\n((?:.{0,80}\n)+))?$"

And even tryed replacing \n with $'\n' but it didn't helped.


Bash supports POSIX extended regular expressions (ERE), not Perl-compatible regexes (PCRE). In particular, (?x) and (?:...) are PCRE.

On a quick look, the one-line version should work if you just replace (?:...) with (...). The "ignore whitespace" function provided by Perl's x modifier isn't available in extended regexes.

See also: Why does my regular expression work in X but not in Y?

  • Thank you @ilkkachu , I replaced non-capturing groups with capturing ones but it didn't help so I also replaced \n with $'\n' ("^(feat|fix|docs|style|refactor|test|chore):"$'\s'"(.{1,50})"$'\n'"("$'\n'"((.{0,80}"$'\n'")+))?$"). Now it works for multiline text inlined in variable when replace the message with an actual value but doesn't work with message=$(cat ${file_name}). Could you, please, advise on it? – Denis Verhoturov Sep 20 '19 at 17:43
  • Changed regexp to "^(feat|fix|docs|style|refactor|test|chore):[ ](.{1,50})("$'\n'"("$'\n'"[^"$'\n'"]{0,80})+)?$" - everything works fine now=) – Denis Verhoturov Sep 20 '19 at 18:48
  • @DenisVerhoturov, oh, sorry, I missed the \n. The command substitution in message=$(cat filename) would remove all trailing newlines from the value, which might affect your original regex – ilkkachu Sep 20 '19 at 23:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.