Skip to content

Commit

Permalink
Adding several file-based functions & tree-rename (#141)
Browse files Browse the repository at this point in the history
  • Loading branch information
kigster authored May 19, 2023
1 parent 697e381 commit aeb271e
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.2.0
3.2.1
60 changes: 29 additions & 31 deletions lib/file.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@

source "${BASHMATIC_HOME}/lib/file-helpers.sh"

# @description Creates a temporary file and returns it as STDOUT
# shellcheck disable=SC2120
# @description Creates a temporary file and returns it as STDOUT # shellcheck disable=SC2120
function file.temp() {
local host="${HOST:-${HOSTNAME:-$(hostname)}}"
local user="${USER:-"$(whoami)"}"
Expand Down Expand Up @@ -34,9 +33,9 @@ function dir.temp() {
trap "rm -rf ${dir}" EXIT
}

file.print-normalized-name() {
function file.print-normalized-name() {
local file="$1"
echo "${file}" | tr '[:upper:]' '[:lower:]' | sed -E 's/ /-/g; s/[^A-Za-z0-9.\-]/-/g; s/---+/--/g'
echo -e "${file}" | ascii-pipe | tr '[:upper:]' '[:lower:]' | sed -E 's/ /-/g; s/[^A-Za-z0-9.\-]/-/g; s/---+/--/g' | tr -d '\r\n'
}

# @description This function will rename all files passed to it as follows: spaces
Expand All @@ -47,7 +46,7 @@ file.print-normalized-name() {
# file.normalize-files "My Word Document.docx"
# # my-word-document.docx
#
file.normalize-files() {
function file.normalize-files() {
trap 'return 1' INT
run.set-all abort-on-error
local file
Expand Down Expand Up @@ -78,15 +77,14 @@ file.normalize-files() {
}

# Replaces a given regex with a string
file.gsub() {
function file.gsub() {
local file="$1"
shift
local find="$1"
shift
local replace="$1"
shift
local
runtime_options="$*"
local runtime_options="$*"

[[ ! -s "${file}" || -z "${find}" || -z "${replace}" ]] && {
error "Invalid usage of file.sub — " \
Expand All @@ -110,7 +108,7 @@ function file.first-is-newer-than-second() {

# Usage:
# (( $(file.exists-and-newer-than "/tmp/file.txt" 30) )) && echo "Yes!"
file.exists-and-newer-than() {
function file.exists-and-newer-than() {
local file="${1}"
shift
local minutes="${1}"
Expand All @@ -123,7 +121,7 @@ file.exists-and-newer-than() {
}

# @description Ask the user whether to overwrite the file
file.ask.if-exists() {
function file.ask.if-exists() {
local file="$1"
shift
local message="$*"
Expand All @@ -148,7 +146,7 @@ file.ask.if-exists() {
# @example
# file.install-with-backup conf/.psqlrc ~/.psqlrc backup-strategy-function
#
file.install-with-backup() {
function file.install-with-backup() {
local source="$1"; shift
if [[ ! -f "${source}" ]]; then
error "file ${source} can not be found"
Expand Down Expand Up @@ -195,22 +193,22 @@ file.install-with-backup() {
}

# @description Prints the file's last modified date
file.last-modified-date() {
function file.last-modified-date() {
stat -f "%Sm" -t "%Y-%m-%d" "$1"
}

# @description Prints the year of the file's last modified date
file.last-modified-year() {
function file.last-modified-year() {
stat -f "%Sm" -t "%Y" "$1"
}

# @description Prints the file's last modified date expressed as millisecondsd
file.last-modified-millis() {
function file.last-modified-millis() {
echo -n "$(/usr/bin/stat -f %m "$1")000"
}

# Return one field of stat -s call on a given file.
file.stat() {
function file.stat() {
local file="$1"
local field="$2"

Expand All @@ -232,7 +230,7 @@ file.stat() {
}

# @description Returns the file size in bytes
file.size() {
function file.size() {
util.os
if [[ ${BASHMATIC_OS} =~ linux ]]; then
stat -c %s "$1"
Expand All @@ -242,7 +240,7 @@ file.size() {
}

# @description Prints the file size expressed in Mb (and up to 1 decimal point)
file.size.mb() {
function file.size.mb() {
local file="$1"
shift
local s=$(file.size "${file}")
Expand All @@ -251,7 +249,7 @@ file.size.mb() {
}

# @description Prints the file size expressed in Gb (and up to 1 decimal point)
file.size.gb() {
function file.size.gb() {
local file="$1"
shift
local s=$(file.size "${file}")
Expand All @@ -260,20 +258,20 @@ file.size.gb() {
}

# @description For each argument prints only those that represent existing files
file.list.filter-existing() {
function file.list.filter-existing() {
for file in "$@"; do
[[ -f "${file}" ]] && echo "${file}"
done
}

# @description For each argument prints only those that represent non-emtpy files
file.list.filter-non-empty() {
function file.list.filter-non-empty() {
for file in "$@"; do
[[ -s "${file}" ]] && echo "${file}"
done
}

file.source-if-exists() {
function file.source-if-exists() {
local file
for file in "$@"; do
[[ -f "${file}" ]] && source "${file}"
Expand Down Expand Up @@ -334,16 +332,16 @@ files.map.shell-scripts() {
files.map "$1" '*.sh' "$2"
}

file.extension.remove() {
function file.extension.remove() {
local filename="$1"
printf "${filename%.*}"
}

file.strip.extension() {
function file.strip.extension() {
file.extension.remove "$@"
}

file.extension() {
function file.extension() {
local filename="$1"
printf "${filename##*.}"
}
Expand All @@ -352,7 +350,7 @@ file.extension() {
# file.extension.replace .sh $(find lib -type f -name '*.bash')
# replaces all files under lib/ mathcing *.sh and renames them
# to the given extension.
file.extension.replace() {
function file.extension.replace() {
local ext="$1"
shift

Expand All @@ -372,37 +370,37 @@ file.extension.replace() {
}

# @description Prints the number of lines in the file
file.count.lines() {
function file.count.lines() {
[[ -f "$1" ]] || return 1
wc -l "$1" | awk '{print $1}' | tr -d '\n'
}

# @description Prints the number of lines in the file
file.count.words() {
function file.count.words() {
[[ -f "$1" ]] || return 1
wc -w "$1" | awk '{print $1}' | tr -d '\n'
}

# @description Invokes UNIX find command searching for files (not folders)
# matching the first argument in the name.
file.find() {
function file.find() {
find . -name "*$1*" -type f -print
}

# @description Invokes UNIX find command searching for folders (not files)
# matching the first argument in the name.
dir.find() {
function dir.find() {
find . -name "*$1*" -type d -print
}

# @description Prints all folders sorted by size, and size printed in Mb
ls.mb(){
function ls.mb(){
# du -k | grep -v '\''./.*\/'\' | sort -n | awk '{ printf("%20.1fMb %s\n", $1/1024, $2 )}' | tail -10
du -m -d 1 "$@" | sort -rn
}

# @description Prints all folders sorted by size, and size printed in Gb
ls.gb(){
function ls.gb(){
# du -k | grep -v '\''./.*\/'\' | sort -n | awk '{ printf("%20.1fGb %s\n", $1/1024/1024, $2 )}' | tail -10
du -g -d 1 "$@" | sort -rn
}
Expand Down
145 changes: 145 additions & 0 deletions lib/files-normalize.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#!/usr/bin/env bash
# @description Renames files matching the input parameters to `find` by
# replacing spaces with dashes and lower casing the file.
function files.normalize-tree() {
local command="__files.normalize-tree"
( "${command}" "$@" )
}

function __files.normalize-tree() {
local dry_run=false
local verbose=false
local interactive=false
local -a delete_files=( .DS_Store )
local -a skip_files=( Icon Icon$(echo -e "\r") )
local -a find_args
find_args=()

while :; do
[[ -z $1 ]] && break
case $1 in
--dry-run|-n)
dry_run=true
shift
;;
--verbose|-v)
verbose=true
shift
;;
--interactive|-i)
interactive=true
shift
;;
--help|-h)
printf "${bldgrn}USAGE:${bldylw}
files.normalize-tree [ --verbose | -v ] [ --dry-run | -n ]
[ --interactive | -i ]
< additional find arguments to find command>
${bldgrn}DESCRIPTION:
${clr}Given the search pattern to find, this function will rename all of the
files matching find parameteres (relative to the current directory)
to a lower case and replace spaces with dashes.\n\n"
return
;;
*)
find_args+=( "$1" )
shift
;;
esac
done

local find_command
if [[ ${find_args[*]} =~ find ]] ; then
find_command="${find_args[*]}"
else
find_command="find . -type f ${find_args[*]}"
fi

info "Files will be searched using the following command:"
h1 "${bldylw}${find_command}"

local -a files
mapfile -t files < <(eval "${find_command}")
if [[ ${#files[@]} -eq 0 ]]; then
error "No files mathed search pattern [$*]" \
"Please make sure that you escape any single quotes, like so:" \
"files.normalize-tree -spaces [ --dry-run ] -name \'*.wav\'"
return 1
else
h4 "Total of ${#files[@]} files matched."
fi

${interactive} && run.ui.ask "Should I proceed with the rename?"

local show_warning=true
run.set-all abort-on-error show-output-on
for file in "${files[@]}"; do
${verbose} && printf "processing file ${bldgrn}%s${clr}\n" "${file}"
[[ -f "${file}" ]] || continue
local command
local file_basename="$(basename "${file}")"
if array.includes "${skip_files[@]}" "${file_basename}"; then
info "File matched one of the skip files, skipping."
continue
elif array.includes "${delete_files[@]}" "${file_basename}"; then
info "File matched one of the delete files, deleting ."
command="rm -fv ${file}"
else
local f="$(echo "${file}" | tr -d '\n')";
local newname="$(echo -e "${f}" | ascii-pipe | tr ' ' '-' | sed 's/--*/-/g' | tr '[:upper:]' '[:lower:]' | tr -d '\r\n')";
local dir="$(dirname "${newname}")"
command="[[ -d \"${dir}\" ]] || mkdir -p \"${dir}\"; mv -vi \"${file}\" \"${newname}\""
fi

${dry_run} && {
info "[dry-run] ❯ ${bldylw}${command}"
continue
}

if ${interactive} ; then
local answer
${show_warning} && {
h3bg "NOTE: if you answer 'a' or 'all' to any of the following questions" \
"the rest of the files will be renamed as if the interactive mode was disabled."
show_warning=false
}
if [[ "${f}" == "${newname}" ]]; then
info "File [$f] is already normalized, skipping."
continue
fi
info "About to rename [$f] into [${newname}]..."
run.ui.ask-user-value answer "Rename the file? ${bldylw}(yes,y/no,n/all,a/quit,q): "
case ${answer} in
q|quit|Q|Quit)
info "Aborting the rename as requested."
exit 1
;;
y|Y|yes|Yes)
run "${command}"
;;
n|N|no|No)
info "Skipping file ${f}..."
continue
;;
a|all|A|All)
info "Turning off interactive mode..."
interactive=false
run "${command}"
;;
*)
error "Answer ${answer} is invalid. Try again."
exit 1
;;
esac
else
if ${verbose}; then
run "${command}"
else
eval "${command}"
fi
sleep 0.05
fi
done
return 0
}

0 comments on commit aeb271e

Please sign in to comment.