#!/bin/sh # Allow bulk operations on a list of selected files. # Requires: dmenu (or similar), trash (optional), open-file (for open only) # Usage: sel [-m menu] [command] # If no command is specified, shows the list of selected files # Commands: # add [files...]: add files to selection # addall: add all files in the current folder to selection # clear: clear selection # cp: copy to current dir, possbily after editing filenames # edit: open selection in editor # mv: move to current dir, possbily after editing filenames # open: open files using open-file # rm: remove selected files and clear selection # TODO: The usage of paste(1) is a bit of a hack, and for example it does # not work if filenames contain tab characters. Fix this. # Bug (probably not fixing this, use virename): moving a file to # itself gives an error and rm is not run. file="$XDG_DATA_HOME/selectedfiles" menu="" # default uses dmenu-filepicker default editor=${EDITOR:-vi} rm="trash rm" # replace with rm -r if you don't use trash usage() { echo "Usage: sel [-m MENU] [COMMAND]" echo "If no COMMAND is specified, shows the list of selected files" echo "Possible commands:" echo " add [files...]: add files to selection" echo " addall: add all files in the current folder to selection" echo " clear: clear selection" echo " cp: copy to current dir, possbily after editing filenames" echo " edit: open selection in editor" echo " mv: move to current dir, possbily after editing filenames" echo " open: open files using open-file" echo " rm: remove selected files and clear selection" } while getopts "m:" opt; do case "$opt" in m) menu="$OPTARG" ;; *) usage exit 1 ;; esac done shift $((OPTIND - 1)) add() { shift 1 if [ -z "$1" ]; then if [ -n "$menu" ]; then dmenu-filepicker -m "$menu" >> "$file" else dmenu-filepicker >> "$file" fi else for i in "$@"; do realpath "$i" >> "$file"; done fi sort -u -o "$file" "$file" } clear() { cat /dev/null > "$file" } cphere() { file2=$(mktemp) sed 's/^.*\///' "$file" > "$file2" $editor "$file2" if [ "$(wc -l "$file" | awk '{print $1}')" != \ "$(wc -l "$file2" | awk '{print $1}')" ]; then echo "Error reading new file names" return 1 else paste "$file" "$file2" | while read -r f; do fold=$(echo "$f" | sed 's/ .*//') fnew=$(echo "$f" | sed 's/.* //') cp -R "$fold" ./"$fnew" || return 1 done fi } open() { while read -r f; do open-file "$f" done < "$file" } remove() { while read -r f; do $rm "$f" done < "$file" } if [ -z "$1" ]; then cat "$file" else case "$1" in add) add "$@" ;; addall) add ./* ;; clear) clear ;; cp) cphere && clear ;; edit) $editor "$file" ;; mv) cphere && (remove; clear) ;; open) open ;; rm) remove && clear ;; *) echo "$1: not a valid sel command" ;; esac fi