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