Skip to content

Instantly share code, notes, and snippets.

@hibiii
Created June 25, 2023 03:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hibiii/f28ca2083fc01ffa0585759efe715d50 to your computer and use it in GitHub Desktop.
Save hibiii/f28ca2083fc01ffa0585759efe715d50 to your computer and use it in GitHub Desktop.
Bash device to allow selecting files for file management in a more interactive way
# Interactive File Management Utilities by hibi
#
# This source file will let you select individual files and copy and move them
# around much like a graphical file manager would. Intended to be used with
# `source` in your bashrc.
#
# Usage:
# Fsel name1 [name2 [...]] - selects files and folders
# Funsel [name1 [name2 [...]]] - unselects files and folders, or all
# Fcp [destination] - copies the files here or to destination
# Fmv [destination] - moves the files here or to destination
# Flsel - lists the current selection
#
# NOTE: This file was made for bash. It is untested in other shells.
# File Select
#
# Selects files by one or more paths or globs, and store their paths into a
# temporary buffer (`_FMG_PATH_BUF`). The previous selection is not cleared, so
# you can select files from different directories. See `Funsel` to clear the
# selection.
function Fsel() {
if [[ ! $1 ]]; then
echo "usage: ${FUNCNAME[0]} name1 [name2 [...]]"
echo "selects files and folders into a buffer to move or paste later"
echo "(places filenames in a temporary buffer, glob matching)"
return 1
fi
local files=("${_FMG_PATH_BUF[@]}")
local found_files=(`command ls -d "$@" 2>/dev/null`)
if [[ $? != 0 ]]; then
echo "error: could not find '$1'"
return 1
fi
for file in "${found_files[@]}"; do
files+=("`realpath "$file"`")
done
# Deduplicate file paths
files=(`printf "%q\n" "${files[@]}" | sort -u`)
export _FMG_PATH_BUF=("${files[@]}")
return 0
}
# File Unselect
#
# Removes files from the current selection. You can use either absolute paths,
# just the filename (or basename), or a glob. If no arguments are provided, then
# the entire selection is cleared.
function Funsel() {
if [[ ! $1 ]]; then
unset _FMG_PATH_BUF
return 0
fi
local files=()
for current_file in "${_FMG_PATH_BUF[@]}"; do
local keep_this=true
for arg_glob in "$@"; do
if [[ $current_file == $arg_glob ]]; then
keep_this=false
fi
done
if [[ "$keep_this" == true ]]; then
files+=("$current_file")
fi
done
export _FMG_PATH_BUF=("${files[@]}")
return 0
}
# Shared code between `Fmv` and `Fcp` functions. Not meant to be used normally.
function _ifmutilInternals@Paste() {
if [[ ${#_FMG_PATH_BUF[@]} < 1 ]]; then
echo "error: there are no selected files"
return 1
fi
local destination
if [[ $2 ]]; then
if [[ -d "$2" || "${#_FMG_PATH_BUF[@]}" == 1 ]]; then
destination="$2"
else
echo "error: cannot copy or move to '$2' with multiple files as it is not a directory"
return 1
fi
else
destination=.
fi
set -e
$1 "${_FMG_PATH_BUF[@]}" "$destination"
set +e
unset _FMG_PATH_BUF
return 0
}
# File Move
#
# Move files to a destination directory, the current one. If given no arguments,
# files are moved to the current directory, otherwise to the given one. If the
# selection is a single file or directory, the destination may be the target
# path for the moved file.
function Fmv() {
if [[ $2 ]]; then
echo "error: too many arguments"
return 1
fi
_ifmutilInternals@Paste mv "$1"
}
# File Copy
#
# Copies files to a destination directory, the current one. If there's a
# directory in the selection, it is copied recursively. If given no arguments,
# files are copied to the current directory, otherwise to the given one. If the
# selection is a single file or directory, the destination may be the target
# path for the copied file.
function Fcp() {
if [[ $2 ]]; then
echo "error: too many arguments"
return 1
fi
_ifmutilInternals@Paste cp\ -r "$1"
}
# File List Selection
#
# List all the files currently selected.
function Flsel() {
if [[ ! $_FMG_PATH_BUF ]]; then
echo "selection is empty"
return 0;
fi
for entry in "${_FMG_PATH_BUF[@]}"; do echo "$entry"; done | column
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment