Search code examples
bashgitsed

How to make sed be aware of the -w flag of git grep?


I thought that with the following code, doing something like git_grep_sed -w pattern replacement would match whole words. However, if say, the match is if, it will also match the substring in fifa. It seems like the sed part isn't actually listening to the -w flag.

git_grep_sed() {
  local grep_options=()
  local search_pattern
  local replacement

  while [[ $1 =~ ^- ]]; do
    grep_options+=("$1")
    shift
  done

  search_pattern=$1
  replacement=$2

  git grep -l "${grep_options[@]}" "$search_pattern" | xargs sed -i "s/$search_pattern/$replacement/g"
}

Why is this, and how to fix it? Maybe I have to use something else instead of sed?


Solution

  • Because you didn't give sed any flags. And in fact there aren't any that would help. Let's say you have a file like

    if consistency == "stiff":
        add_water()
    

    git grep -w will recognise the first line, as it does contain the word if by itself, but sed will just happily change all if instances, because it is unconstrained.

    You need to add word boundary anchors to sed pattern. Unfortunately, it is not as easy as it seems, because different versions of sed do it differently.

    unset use_osx_sed
    if sed -e 's/[[:<:]]//' <<<"" >/dev/null 2>&1
    then
      use_osx_sed=1
    fi
    
    
    git_grep_sed() {
      local grep_options=()
      local search_pattern
      local sed_search_pattern
      local replacement
      local full_word
    
      while [[ $1 =~ ^- ]]; do
        grep_options+=("$1")
        if [[ "$1" == -w ]]
        then
          full_word=1
        fi
        shift
      done
    
      search_pattern="$1"
      sed_search_pattern="$1"
      replacement="$2"
    
      if [[ -n "$full_word" ]]
      then
        if [[ -n "$use_osx_sed" ]]
        then
          sed_search_pattern="[[:<:]]$search_pattern[[:>:]]"
        else
          sed_search_pattern="\\<$search_pattern\\>"
        fi
      fi
    
      git grep -l "${grep_options[@]}" "$search_pattern" | xargs sed -i "s/$sed_search_pattern/$replacement/g"
    }
    

    (The name is probably not accurate. Maybe non_gnu_sed? I don't know how far this reaches, I can only test on OSX and Linux...)