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 esc=$(printf '\033')
  3. declare c_reset="${esc}[0m"
  4. declare c_red="${esc}[31m"
  5. declare dryrun verbose
  6. set -e
  7. err() {
  8. echo -e "${c_red}$1${c_reset}" >&2
  9. }
  10. die() {
  11. exit 1
  12. }
  13. has() {
  14. local verbose=false
  15. if [[ $1 == '-v' ]]; then
  16. verbose=true
  17. shift
  18. fi
  19. for c in "$@"; do c="${c%% *}"
  20. if ! command -v "$c" &> /dev/null; then
  21. [[ "$verbose" == true ]] && 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 browsedir browseinfo query dirs
  48. cwd=$(pwd)
  49. while [[ "$browsedir" != "$cwd" ]]; do
  50. dirs=$( (echo '..'; find -maxdepth 1 -type d 2> /dev/null ) |
  51. sed 's|./||' |
  52. sort -h)
  53. mapfile -t browseinfo < <(
  54. fzf --print-query \
  55. --history="${HOME}/.cache/fzmv_history" \
  56. --header="${errors:-move files here}" <<< "$dirs")
  57. query=${browseinfo[0]}
  58. browsedir=${browseinfo[1]}
  59. [[ -d "$query" ]] && browsedir="$query"
  60. [[ ! -d "$browsedir" ]] && return 1
  61. if [[ "$browsedir" == '.' && $(realpath "$browsedir") != "$cwd" ]]; then
  62. realpath "$browsedir"
  63. break
  64. else
  65. cd "$browsedir" || 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=true ;;
  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[@]}"