Added recu-sed.sh script to 'build' directory.

Details:
- Added a recursive sed script to the 'build' directory.
This commit is contained in:
Field G. Van Zee
2021-12-01 17:36:22 -06:00
parent 12c66a4acc
commit e229e049ca

488
build/recu-sed.sh Executable file
View File

@@ -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 "$@"