.nmv (3723B)
1 #!/usr/bin/env bash 2 3 # Description: An almost fully POSIX compliant batch file renamer 4 # 5 # Note: nnn auto-detects and invokes this plugin if available 6 # Whitespace is used as delimiter for read. 7 # The plugin doesn't support filenames with leading or trailing whitespace 8 # To use NNN_LIST your shell must support readlink(1) 9 # 10 # Capabilities: 11 # 1. Basic file rename 12 # 2. Detects order change 13 # 3. Can move files 14 # 4. Can remove files 15 # 5. Switch number pairs to swap filenames 16 # 17 # Shell: bash 18 # Author: KlzXS 19 20 # shellcheck disable=SC1090,SC1091 21 . "$(dirname "$0")"/.nnn-plugin-helper 22 23 EDITOR="${EDITOR:-vi}" 24 TMPDIR="${TMPDIR:-/tmp}" 25 NNN_INCLUDE_HIDDEN="${NNN_INCLUDE_HIDDEN:-0}" 26 VERBOSE="${VERBOSE:-0}" 27 RECURSIVE="${RECURSIVE:-0}" 28 29 case "$NNN_TRASH" in 30 1) 31 RM_UTIL="trash-put" ;; 32 2) 33 RM_UTIL="gio trash" ;; 34 *) 35 RM_UTIL="rm -ri" ;; 36 esac 37 38 exit_status=0 39 40 if nnn_use_selection "Rename"; then 41 # shellcheck disable=SC2154 42 arr=$(tr '\0' '\n' < "$selection") 43 else 44 findcmd="find . ! -name ." 45 46 if [ "$RECURSIVE" -eq 0 ]; then 47 findcmd="$findcmd -prune" 48 fi 49 50 if [ "$NNN_INCLUDE_HIDDEN" -eq 0 ]; then 51 findcmd="$findcmd ! -name \".*\"" 52 fi 53 54 if [ -z "$NNN_LIST" ]; then 55 findcmd="$findcmd -print" 56 else 57 findcmd="$findcmd -printf "'"'"$NNN_LIST/%P\n"'"' 58 fi 59 60 arr=$(eval "$findcmd" | sort) 61 fi 62 63 lines=$(printf "%s\n" "$arr" | wc -l) 64 width=${#lines} 65 66 dst_file=$(mktemp "$TMPDIR/.nnnXXXXXX") 67 trap 'rm -f "$dst_file"' EXIT 68 69 printf "%s" "$arr" | awk '{printf("%'"${width}"'d %s\n", NR, $0)}' > "$dst_file" 70 71 items=("~") 72 while IFS='' read -r line; do 73 if [ -n "$NNN_LIST" ]; then 74 line=$(readlink "$line" || printf "%s" "$line") 75 fi 76 77 items+=("$line"); 78 done < <(printf "%s\n" "$arr") 79 80 $EDITOR "$dst_file" 81 82 while read -r num name; do 83 if [ -z "$name" ]; then 84 if [ -z "$num" ]; then 85 continue 86 fi 87 88 printf "%s: unable to parse line, aborting\n" "$0" 89 exit 1 90 fi 91 92 # check if $num is an integer 93 if [ ! "$num" -eq "$num" ] 2> /dev/null; then 94 printf "%s: unable to parse line, aborting\n" "$0" 95 exit 1 96 fi 97 98 src=${items[$num]} 99 100 if [ -z "$src" ]; then 101 printf "%s: unknown item number %s\n" "$0" "$num" > /dev/stderr 102 continue 103 elif [ "$name" != "$src" ]; then 104 if [ -z "$name" ]; then 105 continue 106 fi 107 108 if [ ! -e "$src" ] && [ ! -L "$src" ]; then 109 printf "%s: %s does not exit\n" "$0" "$src" > /dev/stderr 110 111 unset "items[$num]" 112 continue 113 fi 114 115 # handle swaps 116 if [ -e "$name" ] || [ -L "$name" ]; then 117 tmp="$name~" 118 c=0 119 120 while [ -e "$tmp" ] || [ -L "$tmp" ]; do 121 c=$((c+1)) 122 tmp="$tmp~$c" 123 done 124 125 if mv "$name" "$tmp"; then 126 if [ "$VERBOSE" -ne 0 ]; then 127 printf "'%s' -> '%s'\n" "$name" "$tmp" 128 fi 129 else 130 printf "%s: failed to rename %s to %s: %s\n" "$0" "$name" "$tmp" "$!" > /dev/stderr 131 exit_status=1 132 fi 133 134 for key in "${!items[@]}"; do 135 if [ "${items[$key]}" = "$name" ]; then 136 items[$key]="$tmp" 137 fi 138 done 139 fi 140 141 dir=$(dirname "$name") 142 if [ ! -d "$dir" ] && ! mkdir -p "$dir"; then 143 printf "%s: failed to create directory tree %s\n" "$0" "$dir" > /dev/stderr 144 exit_status=1 145 elif ! mv -i "$src" "$name"; then 146 printf "%s: failed to rename %s to %s: %s\n" "$0" "$name" "$tmp" "$!" > /dev/stderr 147 exit_status=1 148 else 149 if [ -d "$name" ]; then 150 for key in "${!items[@]}"; do 151 items[$key]=$(printf "%s" "${items[$key]}" | sed "s|^$src\(\$\|\/\)|$name\1|") 152 done 153 154 if [ "$VERBOSE" -ne 0 ]; then 155 printf "'%s' => '%s'\n" "$src" "$name" 156 fi 157 else 158 true 159 if [ "$VERBOSE" -ne 0 ]; then 160 printf "'%s' -> '%s'\n" "$src" "$name" 161 fi 162 fi 163 fi 164 fi 165 166 unset "items[$num]" 167 done <"$dst_file" 168 169 unset "items[0]" 170 for item in "${items[@]}"; do 171 $RM_UTIL "$item" 172 done 173 174 exit $exit_status