nnn

mirror of https://github.com/jarun/nnn
Log | Files | Refs | README | LICENSE

.nmv (3828B)


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