+#!/usr/bin/env bash
+# Desc: Rename files to remove common prefix
+# Usage: triml.sh [FILE] ([FILE]…)
+# Example: triml.sh ./*.txt
+# Version: 0.0.1
+
+yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
+die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status
+must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
+get_prefix() {
+ # Desc: Iteratively identify common prefix
+ # Input: "$@" arguments
+ # Output: stdout
+ local prefix="$1"; # start with first arg
+ shift; # don't compare first to first
+ for arg in "$@"; do
+ while [[ "$arg" != "$prefix"* ]]; do
+ prefix="${prefix%?}"; # trim end of prefix if match
+ done;
+ done;
+ printf "%s" "$prefix";
+}; # Get common prefix of arguments
+show_usage() {
+ # Desc: Display script usage information
+ # Usage: show_usage
+ # Version 0.0.2
+ # Input: none
+ # Output: stdout
+ # Depends: GNU-coreutils 8.30 (cat)
+ cat <<'EOF'
+ USAGE:
+ triml.sh FILE [FILE…]
+
+ OPTIONS:
+ (none)
+
+ EXAMPLE:
+ triml.sh
+ triml.sh ./*.txt
+EOF
+} # Display information on how to use this script.
+prompt_continue() {
+ # Desc: Prompt user to go approve/disapprove or list files to be moved.
+ while true; do
+ read -rp "Rename ${#args[@]} files? (y/N or l/list): " choice;
+ case "${choice,,}" in
+ y|yes)
+ yell "STATUS:Renaming...";
+ return 0;
+ ;;
+ l|list)
+ for i in "${!args[@]}"; do
+ yell "Rename: ${args[i]} -> ${args_defront[i]}";
+ done;
+ ;;
+ n|no)
+ die "FATAL:Operation aborted.";
+ ;;
+ *)
+ yell "Invalid option.";
+ esac;
+ done;
+}; # prompt user to continue operation
+main () {
+ declare -a args args_defront;
+ declare prefix;
+ declare path_dir name_file name_file_new;
+
+ # Check if no arguments are passed
+ if [[ "$#" -le 0 ]]; then
+ ## Act on working dir files
+ mapfile -t args < <(find . -mindepth 1 -maxdepth 1 -type f);
+ else
+ ## Act on argument-specified file paths
+ args=("$@");
+ fi;
+
+ # Check that all args are file paths
+ for arg in "${args[@]}"; do
+ if [[ ! -f "$arg" ]]; then
+ show_usage;
+ die "FATAL:Not a file:${arg}"; fi;
+ done;
+
+ # Get common prefix of all file basenames
+ ## Assemble directory paths and file name arrays
+ for i in "${!args[@]}"; do
+ #args_path[i]="$(dirname "${args[i]}")";
+ args_file[i]="$(basename "${args[i]}")";
+ done;
+ prefix="$(get_prefix "${args_file[@]}")";
+ #declare -p args prefix; # debug
+ if [[ "${#prefix}" -le 0 ]]; then
+ #declare -p prefix; # debug
+ die "FATAL:No file prefix found."; fi;
+
+ # Assemble paths with defronted basenames
+ for i in "${!args[@]}"; do
+ path_dir="$(dirname "${args[i]}")";
+ name_file="$(basename "${args[i]}")";
+ name_file_new="${name_file#"${prefix}"}";
+ args_defront[i]="${path_dir}/${name_file_new}";
+ done;
+
+ # Throw rename warning
+ must prompt_continue;
+
+ # Rename files to defront prefix
+ for i in "${!args[@]}"; do
+ if mv -n "${args[i]}" "${args_defront[i]}"; then
+ yell "STATUS:Moved ${args[i]} to ${args_defront[i]}";
+ else
+ die "FATAL:Failed to move ${args[i]} to ${args_defront[i]}";
+ fi;
+ done;
+};
+main "$@";
+
+# Author: Steven Baltakatei Sandoval
+# License: GPLv3+