Brian Love
Angular + TypeScript Developer in Denver, CO

Find-and-Replace in Multiple SVN Repositories

Reading time ~3 minutes

A bash shell script to find-and-replace in all files in multiple svn repositoroes.

Here is what I wanted the script to do:

  • Prompt the user for the value to find
  • Prompt the user for the value to replace the “find” value with
  • Prompt the user for the SVN commit message
  • For each repository, check out and update a working copy on my machine, then do the find and replace, and finally, commit the changes to the SVN server

Walkthrough

The first thing I needed to do was to define some constant variables to hold some values, such as the root SVN URL, the path to store the working copies, and the names of all of the repositories.

# Define constants
BASE_URL="http://svn.example.com/"
ROOT="/Users/brian/repos/"
REPOSITORIES=("Access" "Accessibility" "Adobe" "Ajax" "Apache" "CSS")

The next thing I do is prompt the user for the necessary values. I am then escaping the find and replace values for any special regular expression characters.

# Prompt the user for the find value
read -p "What value would you like to replace? " find
find_regex=$(echo "${find}"|sed 's!\([]\*\.\$\/&[]\)!\\\1!g')

# Prompt the user for the replace value
read -p "What value do you want to replace this with? " replace
replace_regex=$(echo "${replace}"|sed 's!\([]\*\.\$\/&[]\)!\\\1!g')

# Prompt the user to enter the commit message
read -p "Enter a commit message for all updates: " commit_message

Now it’s time to loop over each of the repositories. Doing the svn checkout and update is easy enough, but the find and replace was a little tricky.

  • First I use the grep command to get a list (character return delimited) of paths to files that contain the find value
  • I then pipe the results of the grep command to the xargs command, which will call the sed command for each file in the list
  • The sed command uses the following flags:
    • -i to do the replacement inline. Note, Mac OS X uses a BSD version of the sed command that requires a value for the -i argument. In this case I am just using an empty string so that no backup files are generated.
    • -e to specify the command. The command starts with “s/” to tell sed to substitute the find values with the replace values. The command also includes the “g” flag so that the find and replace is made globally throughout the file
  • The last thing we need to do is to commit the changes to the repository.
# Loop over each repository
for i in "${REPOSITORIES[@]}"
do
  #set variables for each iteration
  url="${BASE_URL}${i}/trunk"
  path="${ROOT}${i}"

  #do checkout
  echo "checkout:  ${url}"
  svn checkout "${url}" "${path}"

  #do find and replace
  echo "replacing '${find}' with '${replace}'"
  grep -lr --exclude-dir=".svn" -e "${find}" "${path}" | xargs sed -i '' -e "s/${find_regex}/${replace_regex}/g"

  #do commit
  echo "committing"
  svn commit -m "${commit_message}" "${path}"
done

Complete Script

#!/bin/bash

# Define constants
BASE_URL="http://svn.example.com/"
ROOT="/Users/brian/repos/"
REPOSITORIES=("Access" "Accessibility" "Adobe" "Ajax" "Apache" "CSS")

# Prompt the user for the find value
read -p "What value would you like to replace? " find
find_regex=$(echo "${find}"|sed 's!\([]\*\.\$\/&[]\)!\\\1!g')

# Prompt the user for the replace value
read -p "What value do you want to replace this with? " replace
replace_regex=$(echo "${replace}"|sed 's!\([]\*\.\$\/&[]\)!\\\1!g')

# Prompt the user to enter the commit message
read -p "Enter a commit message for all updates: " commit_message

# Let the user know we are starting
echo "Thanks. We are going to replace all instances of '${find}' with '$replace'."
echo "OK, let's check out all of the SVN repositories. Please be patient..."

# Loop over each repository
for i in "${REPOSITORIES[@]}"
do
  #set variables for each iteration
  url="${BASE_URL}${i}/trunk"
  path="${ROOT}${i}"

  #do checkout
  echo "checkout:  ${url}"
  svn checkout "${url}" "${path}"

  #do find and replace
  echo "replacing '${find}' with '${replace}'"
  grep -lr --exclude-dir=".svn" -e "${find}" "${path}" | xargs sed -i '' -e "s/${find_regex}/${replace_regex}/g"

  #do commit
  echo "committing"
  svn commit -m "${commit_message}" "${path}"
done

Download

If you would like to download the full script, here you go:

Download

Just extract the zip and set it to be executable:

$ chmod +x find-and-replace.sh

Brian Love

Hi, I'm Brian. I am interested in TypeScript, Angular and Node.js. I'm married to my best friend Bonnie, I live in Denver and I ski (a lot).