Recursive Search and Replace Bash Script

I had a need to perform a search and replace operation on all files in a directory of files. Effectively I needed a recursive sed. I couldn't find one and wrote this script. This is version .1, but worked for my needs, hopefully others find it useful. It is probably a smart practice to backup your set of files before running any batch operation on them. The script is released under the GPL2 License.

  1. #!/bin/bash
  2.  
  3. # ------------------------------------------------------------------
  4. #
  5. # Shell program to run a search and replace operation on files recursively.
  6. #
  7. # Copyright 2008, Steve Francia <steve.francia+search_and_replace@gmail.com>.
  8. #
  9. # This program is free software; you can redistribute it and/or
  10. # modify it under the terms of the GNU General Public License as
  11. # published by the Free Software Foundation; either version 2 of the
  12. # License, or (at your option) any later version.
  13. #
  14. # This program is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. # General Public License for more details.
  18. #
  19. # Description:
  20. #
  21. #
  22. # Usage:
  23. #
  24. # search_and_replace.sh [ -h | --help ] [OPTIONS] [ARGUMENT1] [ARGUMENT2]
  25. #
  26. # Options:
  27. #
  28. # -h, --help Display this help message and exit.
  29. # --version Display program version.
  30. #
  31. # Arguments:
  32. #
  33. # ARGUMENT1 The token to search for.
  34. # ARGUMENT2 The token to replace it with.
  35. #
  36. # External programs:
  37. #
  38. # getopt - parse command options (enhanced)
  39. # mktemp - make temporary filename (unique)
  40. # sed - stream editor
  41. # basename - strip directory and suffix from filenames
  42. #
  43. # Revision History:
  44. #
  45. # 2008-07-25 Initial Script Created
  46. # See ChangeLog for additional changes
  47. #
  48. #
  49. # ------------------------------------------------------------------
  50.  
  51. ##### Preamble #####
  52.  
  53. # ------------------------------------------------------------------
  54. # Constants
  55. # ------------------------------------------------------------------
  56.  
  57. declare -r PROGNAME=$(basename $0)
  58. declare -r VERSION="0.1"
  59. declare -r DESCRIPTION="a Shell program to run a search and replace operation on files recursively."
  60.  
  61.  
  62. ##### Functions #####
  63.  
  64. # ------------------------------------------------------------------
  65. # Functions
  66. # ------------------------------------------------------------------
  67.  
  68.  
  69. function clean_up() {
  70.  
  71. # ------------------------------------------------------------------
  72. # Function to remove temporary files and other housekeeping
  73. # No arguments
  74. # ------------------------------------------------------------------
  75.  
  76. #rm -f ${TEMP_FILE1}
  77. return
  78.  
  79. } # end of clean_up
  80.  
  81.  
  82. function error_exit() {
  83.  
  84. # ------------------------------------------------------------------
  85. # Function for exit due to fatal program error
  86. # Arguments:
  87. # 1 (optional) string containing descriptive error message
  88. # ------------------------------------------------------------------
  89.  
  90.  
  91. echo "${PROGNAME}: ${1:-"Unknown Error"}" >&2
  92. clean_up
  93. exit 1
  94.  
  95. } # end of error_exit
  96.  
  97.  
  98. function graceful_exit() {
  99.  
  100. # ------------------------------------------------------------------
  101. # Function called for a graceful exit
  102. # No arguments
  103. # ------------------------------------------------------------------
  104.  
  105. clean_up
  106. exit
  107.  
  108. } # end of graceful_exit
  109.  
  110.  
  111. function signal_exit() {
  112.  
  113. # ------------------------------------------------------------------
  114. # Function to handle termination signals
  115. # Arguments:
  116. # 1 (optional) signal_spec
  117. # ------------------------------------------------------------------
  118.  
  119. case $1 in
  120. INT)
  121. echo "$PROGNAME: Program aborted by user" >&2
  122. clean_up
  123. exit
  124. ;;
  125. TERM)
  126. echo "$PROGNAME: Program terminated" >&2
  127. clean_up
  128. exit
  129. ;;
  130. *)
  131. error_exit "$PROGNAME: Terminating on unknown signal"
  132. ;;
  133.  
  134. esac
  135.  
  136. } # end of signal_exit
  137.  
  138.  
  139. function make_temp_files() {
  140.  
  141. # ------------------------------------------------------------------
  142. # Function to create temporary files
  143. # No arguments
  144. # ------------------------------------------------------------------
  145.  
  146. # Use user's local tmp directory if it exists
  147.  
  148. if [ -d ~/tmp ]; then
  149. TEMP_DIR=~/tmp
  150. else
  151. TEMP_DIR=/tmp
  152. fi
  153.  
  154. # Temp file for this script, using paranoid method of creation to
  155. # insure that file name is not predictable. This is for security
  156. # to avoid "tmp race" attacks. If more files are needed, create
  157. # using the same form.
  158.  
  159. TEMP_FILE1=$(mktemp -q "${TEMP_DIR}/${PROGNAME}.$$.XXXXXX")
  160. if [ "$TEMP_FILE1" = "" ]; then
  161. error_exit "cannot create temp file!"
  162. fi
  163.  
  164. } # end of make_temp_files
  165.  
  166.  
  167. function usage() {
  168.  
  169. # ------------------------------------------------------------------
  170. # Function to display usage message (does not exit)
  171. # No arguments
  172. # ------------------------------------------------------------------
  173.  
  174. echo "Usage: ${PROGNAME} [-h | --help] [OPTIONS] [SEARCH] [REPLACE]"
  175.  
  176. } # end of usage
  177.  
  178.  
  179. function helptext() {
  180.  
  181. # ------------------------------------------------------------------
  182. # Function to display help message for program
  183. # No arguments
  184. # ------------------------------------------------------------------
  185.  
  186. cat <<EOF
  187.  
  188. ${PROGNAME} ${VERSION}
  189.  
  190. This is a program to ${DESCRIPTION}.
  191.  
  192. $(usage)
  193.  
  194. Options:
  195.  
  196. -h, --help Display this help message and exit.
  197. -t, --test Display what is actualy being passed to sed.
  198. -e [ext], --extension [ext]
  199. Only replace text found in files found with extension
  200. --version Display program version.
  201.  
  202. Arguments:
  203.  
  204. SEARCH The token to search for.
  205. REPLACE The token to replace it with.
  206.  
  207. Hints:
  208. Please escape your strings. If you are unsure, please test with -t first.
  209.  
  210. EOF
  211.  
  212. } # end of helptext
  213.  
  214. function search_and_replace() {
  215.  
  216. # ------------------------------------------------------------------
  217. # Function to [INSERT FUNCTION DESCRIPTION]
  218. # Arguments:
  219. # 1 (required) arg1
  220. # 2 (required) arg2
  221. # ------------------------------------------------------------------
  222.  
  223. if [ $testmode -ne 0 ] ; then
  224. echo "passing 's/$1/$2/g' into sed"
  225. graceful_exit;
  226. fi
  227.  
  228. # Fatal error if required arguments are missing
  229.  
  230. if [ "$1" = "" ]; then
  231. error_exit "search_and_replace: missing argument 1"
  232. fi
  233. if [ "$2" = "" ]; then
  234. error_exit "search_and_replace: missing argument 2"
  235. fi
  236.  
  237. sed -e 's/'"$1"'/'"$2"'/g' "$file" > $TEMP_FILE1
  238. mv $TEMP_FILE1 "$file"
  239.  
  240. return
  241.  
  242. } # end of search_and_replace
  243.  
  244. # Recursive Directory Traverser
  245. # Author: Kaz Kylheku
  246. # Date: Feb 27, 1999
  247. # Copyright 1999
  248.  
  249. function recurse() {
  250. # Function parameter usage:
  251. # $1 directory to search
  252. # $2 pattern to search for
  253. # $3 command to execute
  254. # $4 secret argument for passing down path
  255.  
  256. if [ "$1" = "" ]; then
  257. error_exit "recurse: missing argument 1"
  258. fi
  259. if [ "$2" = "" ]; then
  260. error_exit "recurse: missing argument 2"
  261. fi
  262. if [ "$3" = "" ]; then
  263. error_exit "recurse: missing argument 3"
  264. fi
  265.  
  266. local file
  267. local path
  268.  
  269. if [ "$4" = "" ] ; then
  270. path="${1%/}/"
  271. else
  272. path="$4$1/"
  273. fi
  274.  
  275. if cd "$1" ; then
  276. for file in $2; do
  277. if [ -f "$file" -o -d "$file" ]; then
  278. eval "$3"
  279. fi
  280. done
  281. for file in .* * ; do
  282. if [ "$file" = "." -o "$file" = ".." ] ; then
  283. continue
  284. fi
  285. if [ -d "$file" -a ! -L "$file" ]; then
  286. recurse "$file" "$2" "$3" "$path"
  287. fi
  288. done
  289. cd ..
  290. fi
  291. }
  292.  
  293.  
  294. # ------------------------------------------------------------------
  295. # Program starts here
  296. # ------------------------------------------------------------------
  297.  
  298. ##### Initialization And Setup #####
  299.  
  300. ## Set file creation mask so that all files are created with 600
  301. ## permissions.
  302. ##
  303. #umask 066
  304.  
  305. # Trap TERM, HUP, and INT signals and properly exit
  306.  
  307. trap "signal_exit TERM" TERM HUP
  308. trap "signal_exit INT" INT
  309.  
  310. ## Create temporary file(s)
  311. #
  312. make_temp_files
  313.  
  314.  
  315. ##### Command Line Processing #####
  316.  
  317. # if at least one argument is required...
  318. if [ $# -eq 0 ]; then
  319. echo "This is ${PROGNAME} ${VERSION}"
  320. usage
  321. clean_up
  322. exit 1
  323. fi
  324.  
  325. # Note that we use `"$@"' to let each command-line parameter expand to
  326. # a separate word. The quotes around `$@' are essential! We need
  327. # GETOPT_TEMP as the `eval set --' would nuke the return value of
  328. # getopt.
  329. GETOPT_TEMP=$(getopt -o +te:h --long test,extension:,help,version -n "$PROGNAME" -- "$@")
  330.  
  331. if [ $? != 0 ] ; then
  332. error_exit "Error parsing command line. Terminating..."
  333. fi
  334.  
  335. # Note the quotes around `$GETOPT_TEMP': they are essential!
  336. eval set -- "$GETOPT_TEMP"
  337.  
  338. # no error checking necessary; sanity of command line and required
  339. # arguments has been checked by getopt program
  340.  
  341. ext="*";
  342. testmode=0;
  343.  
  344. while true ; do
  345. case $1 in
  346. -t|--test)
  347. echo "TEST MODE" ;
  348. testmode=1;
  349. shift
  350. ;;
  351. -e|--extension)
  352. echo "search for extension: $2"
  353. ext=$2
  354. shift 2
  355. ;;
  356. -h|--help)
  357. helptext ;
  358. graceful_exit
  359. ;;
  360. --version)
  361. echo "${PROGNAME} ${VERSION}" ;
  362. shift
  363. ;;
  364. --)
  365. shift ;
  366. break
  367. ;;
  368. *)
  369. # should be impossible to reach: getopt should have caught
  370. # an error
  371. error_exit "This should not have happened; unknown option '$1'. Terminating..."
  372. ;;
  373. esac
  374. done
  375. unset GETOPT_TEMP
  376.  
  377. # processing remaining arguments for the client
  378. if [ $# -ne 0 ]; then
  379. echo "Searching for: $@"
  380. fi
  381.  
  382.  
  383. ##### Main Logic #####
  384.  
  385. recurse `/bin/pwd` "*.$ext" "search_and_replace $1 $2"
  386.  
  387. graceful_exit
  388.  
  389. # end of search_and_replace.sh
AttachmentSize
search_and_replace.sh.zip3.54 KB

Post new comment