#!/usr/bin/env sh # Description: An almost fully POSIX compliant batch file renamer # # Note: nnn auto-detects and invokes this plugin if available # # Capabilities: # 1. Basic file rename # 2. Detects order change # 3. Can move files # 4. Can remove files # 5. Switch number pairs to swap filenames # # Shell: POSIX compliant # Author: KlzXS EDITOR="${EDITOR:-vi}" TMPDIR="${TMPDIR:-/tmp}" INCLUDE_HIDDEN="${INCLUDE_HIDDEN:-0}" selection=${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection exit_status=0 dst_file=$(mktemp "$TMPDIR/.nnnXXXXXX") if [ -s "$selection" ]; then printf "Rename 'c'urrent / 's'election? " read -r resp fi if [ "$resp" = "s" ]; then arr=$(tr '\0' '\n' < "$selection") else if [ "$INCLUDE_HIDDEN" -eq 0 ]; then arr=$(find . ! -name . -prune ! -name ".*" -print | sort) else arr=$(find . ! -name . -prune -print | sort) fi fi printf "%s" "$arr" | awk '{print NR " " $0}' > "$dst_file" arr=$(printf "%s" "$arr" | sed "s/'/'\\\\''/g;s/^\(.*\)$/'\1'/" | tr '\n' ' ') eval "set -- $arr" $EDITOR "$dst_file" while read -r num name; do if [ -z "$name" ]; then if [ -z "$num" ]; then continue fi printf "%s: unable to parse line, aborting\n" "$0" exit 1 fi # check if $num is an integer if [ ! "$num" -eq "$num" ] 2> /dev/null; then printf "%s: unable to parse line, aborting\n" "$0" exit 1 fi src=$(eval printf "%s" "\"\${$num}\"") if [ -z "$src" ]; then printf "%s: unknown item number %s\n" "$0" "$num" > /dev/stderr continue elif [ "$name" != "$src" ]; then if [ -z "$name" ]; then continue fi if [ ! -e "$src" ] && [ ! -L "$src" ]; then printf "%s: %s does not exit\n" "$0" "$src" > /dev/stderr c=1 new_args="" while [ $c -le $# ]; do tmp=$(eval printf "%s" "\"\${$c}\"") if [ "$c" -eq "$num" ]; then new_args="$new_args ''" else if [ -z "$tmp" ]; then tmp="''" else tmp=$(printf "%s" "$tmp" | sed "s/'/'\\\\''/g;s/^\(.*\)$/'\1'/") fi new_args="$new_args $tmp" fi c=$((c+1)) done eval "set -- $new_args" continue fi # handle swaps if [ -e "$name" ] || [ -L "$name" ]; then tmp="$name~" c=0 while [ -e "$tmp" ] || [ -L "$tmp" ]; do c=$((c+1)) tmp="$tmp~$c" done if mv "$name" "$tmp"; then printf "'%s' -> '%s'\n" "$name" "$tmp" else printf "%s: failed to rename %s to %s: %s\n" "$0" "$name" "$tmp" "$!" > /dev/stderr exit_status=1 fi c=1 new_args="" while [ $c -le $# ]; do item=$(eval printf "%s" "\"\${$c}\"") if [ "$item" = "$name" ]; then item=$(printf "%s" "$tmp" | sed "s/'/'\\\\''/g;s/^\(.*\)$/'\1'/") new_args="$new_args $item" else if [ -z "$item" ]; then item="''" else item=$(printf "%s" "$item" | sed "s/'/'\\\\''/g;s/^\(.*\)$/'\1'/") fi new_args="$new_args $item" fi c=$((c+1)) done eval "set -- $new_args" fi dir=$(dirname "$name") if [ ! -d "$dir" ] && ! mkdir -p "$dir"; then printf "%s: failed to create directory tree %s\n" "$0" "$dir" > /dev/stderr exit_status=1 elif ! mv "$src" "$name"; then printf "%s: failed to rename %s to %s: %s\n" "$0" "$name" "$tmp" "$!" > /dev/stderr exit_status=1 else printf "'%s' -> '%s'\n" "$src" "$name" if [ -d "$name" ]; then c=1 new_args="" while [ $c -le $# ]; do tmp=$(eval printf "\"\${$c}\"") if [ -z "$tmp" ]; then tmp="''" else tmp=$(printf "%s" "$tmp" | sed "s|^$src\(\$\|\/\)|$name\1|;s/'/'\\\\''/g;s/^\(.*\)$/'\1'/") fi new_args="$new_args $tmp" c=$((c+1)) done eval "set -- $new_args" printf "'%s' => '%s'\n" "$src" "$name" fi fi fi c=1 new_args="" while [ $c -le $# ]; do tmp=$(eval printf "%s" "\"\${$c}\"") if [ "$c" -eq "$num" ]; then new_args="$new_args ''" else if [ -z "$tmp" ]; then tmp="''" else tmp=$(printf "%s" "$tmp" | sed "s/'/'\\\\''/g;s/^\(.*\)$/'\1'/") fi new_args="$new_args $tmp" fi c=$((c+1)) done eval "set -- $new_args" done <"$dst_file" c=1 new_args="" while [ $c -le $# ]; do tmp=$(eval printf "%s" "\"\${$c}\"") if [ -n "$tmp" ]; then rm -ri "$tmp" fi c=$((c+1)) done rm "$dst_file" exit $exit_status