diff --git a/build/recu-sed.sh b/build/recu-sed.sh new file mode 100755 index 000000000..e7a1d43db --- /dev/null +++ b/build/recu-sed.sh @@ -0,0 +1,488 @@ +#!/bin/bash + +# +# recursive-sed.sh +# +# Field G. Van Zee +# + +print_usage() +{ + # Echo usage info + echo " " + echo " "$script_name + echo " " + echo " Field G. Van Zee" + echo " " + echo " Recusively descend a directory tree and perform sed commands, either on" + echo " the filename or the file contents, or both." + echo " " + echo " Usage:" + echo " ${script_name} [options]" + echo " " + echo " The following options are accepted:" + echo " " + echo " -d " + echo " Dry run. Go through all the motions, but don't actually" + echo " apply any of the sed expressions to file names or contents." + echo " -N " + echo " Do not proceed recursively into subdirectories; consider" + echo " only the files within the current directory. Default" + echo " behavior is to act recursively." + echo " -h " + echo " Consider hidden files and directories. Default behavior is" + echo " to ignore them." + echo " -n " + echo " Use svn mv instead of mv when renaming the file." + echo " Notice that this only applies if the filename changes." + echo " -p pattern " + echo " Specifies the filename pattern, as would be given to the" + echo " ls utility, to limit which files are affected. Default is" + echo " the to consider all files present." + echo " -r dir" + echo " The root directory for the recursive action to be performed." + echo " Default is to use the current working directory." + echo " -v [0|1|2]" + echo " verboseness level" + echo " level 0: silent (no output)" + echo " level 1: default (one line per directory; supress ls stderr)" + echo " level 2: verbose (one line per directory; show ls stderr)" + echo " " + echo " At least one of the following option-argument pairs is required:" + echo " " + echo " -f sed_expr " + echo " Specifies the sed expression that will be applied to the" + echo " filenames of the files touched by the script. This expression" + echo " must be a search-and-replace pattern." + echo " -c sed_expr " + echo " Specifies the sed expression that will be applied to the" + echo " contents of the files touched by the script. This expression" + echo " should be a search-and-replace pattern." + echo " -s sed_script" + echo " Specifies an arbitrary sed script that will be applied to the" + echo " file contents of the files touched by the script." + echo " " + echo " Note: -c and -s options are mutually exclusive." + echo " " + + # Exit with non-zero exit status + exit 1 +} + + + + +perform_sed() +{ + # Variables set by getopts. + local exist_dir="$1" + + #echo "exist_dir: $exist_dir" + + # The suffix used to create temporary files + local temp_file_suffix="sed_temp" + + # Check that exist_dir actually exists and is a directory + if [ ! -d "${exist_dir}" ]; then + echo "${script_name}: ${exist_dir} does not seem to be a valid directory." + exit 1 + fi + + # Check that the filename sed expression, if given, begins with an 's'. + if [ -n "$filename_sed_expr" ]; then + + # If it's a valid search-and-replace expression, this should return an 's'. + filename_sed_char=${filename_sed_expr%%/*} + + if [ "$filename_sed_char" != "s" ]; then + echo "${script_name}: sed expression given with -f must be search-and-replace." + exit 1 + fi + fi + + # Check that the sed script, if given, exists. + if [ -n "$contents_sed_script" ]; then + + if [ ! -f ${contents_sed_script} ]; then + echo "${script_name}: ${contents_sed_script} is not a regular file or does not exist." + exit 1 + fi + fi + + # Assume that the sed expression is a search-and-replace. Extract the patterns + # to match on. (Arbitrary sed expressions should be applied through a sed script.) + if [ "$filename_sed_expr" != "" ]; then + filename_sed_match=${filename_sed_expr#s/} + filename_sed_match=${filename_sed_match%%/*} + fi + + + # Get the list of source files in the directory given. Supress stderr if + # level 0 or 1 verbosity was requested. + #if [ "$verbose_level" != "2" ]; then + # old_filepaths=$(ls -d -b ${exist_dir}/${filename_pattern} 2> /dev/null) + #else + # old_filepaths="$(ls -d -b ${exist_dir}/${filename_pattern})" + #fi + + #echo $old_filepaths + #echo "$exist_dir/$filename_pattern" + + #for old_filepath in $old_filepaths; do + #echo "exist_dir: $exist_dir" + + # Find all files that match the pattern in the current directory. + find "${exist_dir}" -maxdepth 1 -name "${filename_pattern}" -print | while read old_filepath + do + #echo "old_filepath: $old_filepath" + + # Skip the current directory. + if [ "${old_filepath}" == "${exist_dir}" ]; then + continue + fi + + # Skip any non-regular files. + if [ ! -f "$old_filepath" ]; then + + # And say we are doing so if verboseness was requested. + if [ "$verbose_level" = "2" ]; then + echo "${script_name}: Ignoring $old_filepath" + fi + continue + fi + + # Strip exist_dir from filename. + old_filename=${old_filepath##*/} + + # Strip the filename from old_filepath to leave the directory path. + old_dirpath=${old_filepath%/*} + + # Create a new filename from the old one. If a filename sed expression was given, + # it will be applied now. + if [ "$filename_sed_expr" != "" ]; then + new_filename=$(echo "${old_filename}" | sed "${filename_sed_expr}") + else + new_filename="${old_filename}" + fi + + #echo "new_filename: $new_filename" + + # Create the filepath to the new file location. + new_filepath="${old_dirpath}/${new_filename}" + #echo "new_filepath: $new_filepath" + + # Grep for the filename pattern within the filename of the current file. + if [ "$filename_sed_expr" != "" ]; then + grep_filename=$(echo "${old_filename}" | grep "${filename_sed_match}") + fi + + + # If we are not performing a dry run, proceed. + if [ -z "$dry_run_flag" ]; then + + # Save the old file permissions so we can re-apply them to the + # new file if its contents change (ie: if it's not just a 'mv', + # which inherently preserves file permissions). + old_perms=$(stat -c %a "${old_filepath}") + + # If the old and new filepaths are different, then we start off by + # renaming the file. (Otherwise, if the old and new filepaths are + # identical, then we don't need to do anything to the file.) If + # the user requested that we use svn mv, then do that, otherwise we + # use regular mv. + if [ "${old_filepath}" != "${new_filepath}" ]; then + + if [ -n "$use_svn_mv_flag" ]; then + + svn mv "${old_filepath}" "${new_filepath}" + else + + mv -f "${old_filepath}" "${new_filepath}" + fi + fi + #else + + # A dry run still needs the act upon the "new" file, so if the + # filepaths are different, simply set the new filepath to the + # old one. (We won't need the previous value of new_filepath + # anymore.) + #if [ "${old_filepath}" != "${new_filepath}" ]; then + # new_filepath="${old_filepath}" + #fi + fi + + # Handle the cases that might change the contents of the file. + if [ "$contents_sed_expr" != "" ] || + [ "$contents_sed_script" != "" ]; then + + # Execute the sed command based on whether the sed action was given + # as a command line expression or a script residing in a file. + if [ "$contents_sed_script" != "" ]; then + + # Perform the action, saving the result to a temporary file. + cat "${new_filepath}" | sed -f ${contents_sed_script} \ + > ${new_filepath}.${temp_file_suffix} + + elif [ "$contents_sed_expr" != "" ]; then + + # Perform the action, saving the result to a temporary file. + cat "${new_filepath}" | sed -e "${contents_sed_expr}" \ + > ${new_filepath}.${temp_file_suffix} + fi + + # Check the difference. + file_diff=$(diff "${new_filepath}" "${new_filepath}.${temp_file_suffix}") + + + # If we are not performing a dry run, proceed. + if [ -z "$dry_run_flag" ]; then + + # If the file contents change. + if [ -n "$file_diff" ]; then + + # Apply the old file permissions to the new file (before we + # potentially overwrite the old file with the new one). + chmod ${old_perms} "${new_filepath}.${temp_file_suffix}" + + # Apply the file contents changes to the new filepath (which may + # or may not be the same as the old filepath). + mv -f "${new_filepath}.${temp_file_suffix}" "${new_filepath}" + + else + # Otherwise remove the new temporary file since it is identical + # to the original. + rm -f "${new_filepath}.${temp_file_suffix}" + fi + else + # Simply remove the file since we are only performing a dry run. + rm -f "${new_filepath}.${temp_file_suffix}" + fi + + fi + + # Check for dos2unix. If it's not here, we'll just substitute cat. + #type_dos2unix=$(type -path dos2unix) + #if [ -n "$type_dos2unix" ]; then + # dos2unix -q ${new_filepath} + #fi + + # Create a string that indicates what we are changing. We'll use this in + # the verbose progress echo to indicate how the file is or would be changed. + if [ -n "$grep_filename" ] && [ -n "$file_diff" ]; then + which_matches="filename/contents" + file_touched="yes" + elif [ -n "$grep_filename" ] && [ -z "$file_diff" ]; then + which_matches="filename " + file_touched="yes" + elif [ -z "$grep_filename" ] && [ -n "$file_diff" ]; then + which_matches=" contents" + file_touched="yes" + else + which_matches="" + file_touched="no" + fi + + # Be verbose, if requested, about which file we're looking at. + if [ "$verbose_level" != "0" ]; then + + # But we only need to output a line if the file was touched. + if [ "$file_touched" != "no" ]; then + + # Construct a relative filepath by stripping the initial root + # directory so that the output does not span as many columns on + # the terminal. + rel_old_filepath=${old_filepath#${initial_root_dir}/} + + # Add a "dry run" condition to the output if we're doing a dry-run + # so that the user knows we didn't really change anything. + if [ -z "$dry_run_flag" ]; then + echo "$script_name: Changing [${which_matches}] of ${rel_old_filepath}" + else + echo "$script_name: Changing (dry run) [${which_matches}] of ${rel_old_filepath}" + fi + fi + fi + + done + + # Exit peacefully. + return 0 +} + + + + +recursive_sed() +{ + # Local variable declarations + local item sub_items curr_dir this_dir + + + # Extract our argument + curr_dir="$1" + + + # Call our function to perform the sed operations on the files in the + # directory given. + perform_sed "${curr_dir}" + + + # If we were asked to act recursively, then continue processing + # curr_dir's contents. + if [ "$recursive_flag" = "1" ]; then + + # Get a listing of items in the directory according to the hidden + # files/directories flag. + if [ -n "$hidden_files_dirs_flag" ]; then + + # Get a listing of the directories in curr_dir (including hidden + # files and directories). + sub_items=$(ls -a "$curr_dir") + + else + + # Get a listing of the directories in curr_dir. + sub_items=$(ls "$curr_dir") + fi + + #echo "sub_items: $sub_items" + + # Descend into the contents of curr_dir, calling recursive_sed on + # any items that are directories. + find "${curr_dir}" -maxdepth 1 -name "*" -print | while read item + do + + #echo "conisdering item: $item" + + # Skip the current directory. + if [ "${item}" == "${curr_dir}" ]; then + continue + fi + + # If item is a directory, descend into it. + if [ -d "$item" ]; then + + #echo "item is dir: $item" + + recursive_sed "$item" + fi + done + + fi + + + # Return peacefully + return 0 +} + + + + +main() +{ + # Variables set by getopts. + dry_run_flag="" + hidden_files_dirs_flag="" + use_svn_mv_flag="" + filename_pattern="" + root_dir="" + initial_root_dir="" + verbose_level="" + filename_sed_expr="" + contents_sed_expr="" + contents_sed_script="" + + recursive_flag="1" + + + # Get the script name + script_name=${0##*/} + + + # Local variable declarations. + local item sub_items this_dir + + + # Process our command line options. + while getopts ":c:df:hp:r:s:nNv:" opt; do + case $opt in + d ) dry_run_flag="1" ;; + h ) hidden_files_dirs_flag="1" ;; + n ) use_svn_mv_flag="1" ;; + N ) recursive_flag="0" ;; + v ) verbose_level="$OPTARG" ;; + p ) filename_pattern="$OPTARG" ;; + r ) root_dir="$OPTARG" ;; + f ) filename_sed_expr="$OPTARG" ;; + c ) contents_sed_expr="$OPTARG" ;; + s ) contents_sed_script="$OPTARG" ;; + \? ) print_usage + esac + done + shift $(($OPTIND - 1)) + + + # Make sure we've parsed all command line arguments by now. + if [ $# != "0" ]; then + echo "${script_name}: Unparsed command line arguments! Try running with no arguments for help." + exit 1 + fi + + + # Make sure we received at least one of the required options. + if [ -z "$filename_sed_expr" ] && + [ -z "$contents_sed_expr" ] && + [ -z "$contents_sed_script" ]; then + print_usage + fi + + + # Make sure that both a file contents sed expression and sed script were + # not given. + if [ "$contents_sed_expr" != "" ] && + [ "$contents_sed_script" != "" ] ; then + echo "${script_name}: The -c and -s options may not be used at the same time." + exit 1 + fi + + + # Make sure that verboseness level is valid. + if [ "$verbose_level" != "0" ] && + [ "$verbose_level" != "1" ] && + [ "$verbose_level" != "2" ]; then + verbose_level="1" + fi + + # Prepare the filename pattern arguments to perform_sed(). + if [ "$filename_pattern" = "" ] ; then + filename_pattern='*' + fi + + # Prepare the directory arguments to perform_sed(). + if [ "$root_dir" != "" ] ; then + + # Strip / from end of directory paths, if there is one. + root_dir=${root_dir%/} + else + root_dir=$PWD + fi + initial_root_dir=${root_dir} + + + #echo "root_dir: $root_dir" + + + # Begin recursing on the root directory. + recursive_sed "$root_dir" + + + # Exit peacefully + return 0 +} + + + + +# The script's main entry point, passing all parameters given. +main "$@" +