.nmv (3835B)
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 EDITOR="${EDITOR:-vi}" 21 TMPDIR="${TMPDIR:-/tmp}" 22 NNN_INCLUDE_HIDDEN="${NNN_INCLUDE_HIDDEN:-0}" 23 VERBOSE="${VERBOSE:-0}" 24 RECURSIVE="${RECURSIVE:-0}" 25 26 case "$NNN_TRASH" in 27 1) 28 RM_UTIL="trash-put" ;; 29 2) 30 RM_UTIL="gio trash" ;; 31 *) 32 RM_UTIL="rm -ri" ;; 33 esac 34 35 selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} 36 exit_status=0 37 38 dst_file=$(mktemp "$TMPDIR/.nnnXXXXXX") 39 40 if [ -s "$selection" ]; then 41 printf "Rename 'c'urrent / 's'election? " 42 read -r resp 43 44 if ! [ "$resp" = "c" ] && ! [ "$resp" = "s" ]; then 45 exit 1 46 fi 47 fi 48 49 if [ "$resp" = "s" ]; then 50 arr=$(tr '\0' '\n' < "$selection") 51 else 52 findcmd="find . ! -name ." 53 54 if [ "$RECURSIVE" -eq 0 ]; then 55 findcmd="$findcmd -prune" 56 fi 57 58 if [ "$NNN_INCLUDE_HIDDEN" -eq 0 ]; then 59 findcmd="$findcmd ! -name \".*\"" 60 fi 61 62 if [ -z "$NNN_LIST" ]; then 63 findcmd="$findcmd -print" 64 else 65 findcmd="$findcmd -printf "'"'"$NNN_LIST/%P\n"'"' 66 fi 67 68 arr=$(eval "$findcmd" | sort) 69 fi 70 71 lines=$(printf "%s\n" "$arr" | wc -l) 72 width=${#lines} 73 74 printf "%s" "$arr" | awk '{printf("%'"${width}"'d %s\n", NR, $0)}' > "$dst_file" 75 76 items=("~") 77 while IFS='' read -r line; do 78 if [ -n "$NNN_LIST" ]; then 79 line=$(readlink "$line" || printf "%s" "$line") 80 fi 81 82 items+=("$line"); 83 done < <(printf "%s\n" "$arr") 84 85 $EDITOR "$dst_file" 86 87 while read -r num name; do 88 if [ -z "$name" ]; then 89 if [ -z "$num" ]; then 90 continue 91 fi 92 93 printf "%s: unable to parse line, aborting\n" "$0" 94 exit 1 95 fi 96 97 # check if $num is an integer 98 if [ ! "$num" -eq "$num" ] 2> /dev/null; then 99 printf "%s: unable to parse line, aborting\n" "$0" 100 exit 1 101 fi 102 103 src=${items[$num]} 104 105 if [ -z "$src" ]; then 106 printf "%s: unknown item number %s\n" "$0" "$num" > /dev/stderr 107 continue 108 elif [ "$name" != "$src" ]; then 109 if [ -z "$name" ]; then 110 continue 111 fi 112 113 if [ ! -e "$src" ] && [ ! -L "$src" ]; then 114 printf "%s: %s does not exit\n" "$0" "$src" > /dev/stderr 115 116 unset "items[$num]" 117 continue 118 fi 119 120 # handle swaps 121 if [ -e "$name" ] || [ -L "$name" ]; then 122 tmp="$name~" 123 c=0 124 125 while [ -e "$tmp" ] || [ -L "$tmp" ]; do 126 c=$((c+1)) 127 tmp="$tmp~$c" 128 done 129 130 if mv "$name" "$tmp"; then 131 if [ "$VERBOSE" -ne 0 ]; then 132 printf "'%s' -> '%s'\n" "$name" "$tmp" 133 fi 134 else 135 printf "%s: failed to rename %s to %s: %s\n" "$0" "$name" "$tmp" "$!" > /dev/stderr 136 exit_status=1 137 fi 138 139 for key in "${!items[@]}"; do 140 if [ "${items[$key]}" = "$name" ]; then 141 items[$key]="$tmp" 142 fi 143 done 144 fi 145 146 dir=$(dirname "$name") 147 if [ ! -d "$dir" ] && ! mkdir -p "$dir"; then 148 printf "%s: failed to create directory tree %s\n" "$0" "$dir" > /dev/stderr 149 exit_status=1 150 elif ! mv -i "$src" "$name"; then 151 printf "%s: failed to rename %s to %s: %s\n" "$0" "$name" "$tmp" "$!" > /dev/stderr 152 exit_status=1 153 else 154 if [ -d "$name" ]; then 155 for key in "${!items[@]}"; do 156 items[$key]=$(printf "%s" "${items[$key]}" | sed "s|^$src\(\$\|\/\)|$name\1|") 157 done 158 159 if [ "$VERBOSE" -ne 0 ]; then 160 printf "'%s' => '%s'\n" "$src" "$name" 161 fi 162 else 163 true 164 if [ "$VERBOSE" -ne 0 ]; then 165 printf "'%s' -> '%s'\n" "$src" "$name" 166 fi 167 fi 168 fi 169 fi 170 171 unset "items[$num]" 172 done <"$dst_file" 173 174 unset "items[0]" 175 for item in "${items[@]}"; do 176 $RM_UTIL "$item" 177 done 178 179 rm "$dst_file" 180 exit $exit_status