You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

fzmv 1.9KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. #!/usr/bin/env bash
  2. declare -r esc=$'\033'
  3. declare -r c_reset="${esc}[0m"
  4. declare -r c_red="${esc}[31m"
  5. declare dryrun verbose
  6. set -e
  7. err() {
  8. printf "${c_red}%s${c_reset}\n" "$*" >&2
  9. }
  10. die() {
  11. exit 1
  12. }
  13. has() {
  14. local verbose=0
  15. if [[ $1 == '-v' ]]; then
  16. verbose=1
  17. shift
  18. fi
  19. for c; do c="${c%% *}"
  20. if ! command -v "$c" &> /dev/null; then
  21. (( verbose > 0 )) && err "$c not found"
  22. return 1
  23. fi
  24. done
  25. }
  26. has -v fzf || die
  27. fzf() {
  28. command fzf --cycle "$@"
  29. }
  30. pick_files() {
  31. local files fzpick
  32. mapfile -t files < <(find . -maxdepth 1 2> /dev/null | sort -h | sed '1d; s|^\./||')
  33. fzpick=$( for f in "${files[@]}"; do
  34. if [[ -d "$f" ]]; then
  35. printf '%s/\n' "$f"
  36. elif [[ -L "$f" ]]; then
  37. printf '%s@\n' "$f"
  38. else
  39. printf '%s\n' "$f"
  40. fi
  41. done | fzf --multi --header='move these files' ) || return 1
  42. for f in "${fzpick[@]}"; do
  43. realpath -s "$f"
  44. done
  45. }
  46. pick_destination() {
  47. local cwd browse_dir browse_info query dirs
  48. cwd=$(pwd)
  49. while [[ "$browse_dir" != "$cwd" ]]; do
  50. dirs=$( (echo '..'; find -maxdepth 1 -type d 2> /dev/null ) |
  51. sed 's|./||' |
  52. sort -h)
  53. mapfile -t browse_info < <(
  54. fzf --print-query \
  55. --history="${HOME}/.cache/fzmv_history" \
  56. --header="${errors:-move files here}" <<< "$dirs")
  57. query=${browse_info[0]}
  58. browse_dir=${browse_info[1]}
  59. [[ -d "$query" ]] && browse_dir="$query"
  60. [[ ! -d "$browse_dir" ]] && return 1
  61. if [[ "$browse_dir" == '.' && $(realpath "$browse_dir") != "$cwd" ]]; then
  62. realpath "$browse_dir"
  63. break
  64. else
  65. cd "$browse_dir" || die
  66. continue
  67. fi
  68. done
  69. }
  70. while (( $# > 0 )); do
  71. case $1 in
  72. -t|--test) dryrun=true ;;
  73. -v|--verbose) verbose=1 ;;
  74. esac
  75. shift
  76. done
  77. mapfile -t files < <(pick_files)
  78. (( ${#files[@]} > 0 )) || exit 1
  79. destination=$(pick_destination) || exit 1
  80. ${dryrun:+echo} mv ${verbose:+-v} -t "$destination" "${files[@]}"