{ ... }: { text = '' # Rename dirs to `snake_case` and files to `PascalCase`. Careful with structured file names like archives! # Usage: name [FILES] function name() { local IFS=$'\n' local targets=(''${@}) [[ "''${targets}" = "" ]] && targets=($(ls)) process() { # Skip archive. if $(_is_archive ''${target}); then _iterate_skip "File is an archive, skip." return 0 fi if [[ -d "''${target}" ]]; then local new_name=$(parse_snake ''${target}) [[ -e "''${new_name}" ]] && return 0 mv -- ''${target} ''${new_name} && echo ''${new_name} else local ext=".''${target##*.}" local name=''${target%.*} [[ "''${ext}" = ".''${target}" ]] && ext="" local new_name="$(parse_pascal ''${name})''${ext}" [[ -e "''${new_name}" ]] && return 0 mv -- ''${target} ''${new_name} && echo ''${new_name} fi } _iterate_targets process ''${targets[@]} } # Rename files with provided parser, i.e. `parse_simple`. # All files by default. # Usage: name_parse [FILES] function name_parse() { local IFS=$'\n' local parser=''${1} local targets=(''${@:2}) [[ "''${targets}" = "" ]] && targets=([^.]*) if [[ "''${parser}" = "" ]]; then help name_parse return 2 fi process() { # Skip archive. if $(_is_archive ''${target}); then _iterate_skip "File is an archive, skip." return 0 fi # parse new name. local ext="" local name="''${target}" # ext only for files. if [[ -f "''${target}" ]]; then ext=".''${target##*.}" name="''${target%.*}" fi # Files w/o extension support. [[ "''${ext#.}" = "''${name}" ]] && ext="" # Get new name. local new_name=$(''${parser} "''${name}")''${ext,,} # check if same name. [[ "''${target}" = "''${new_name}" ]] && return 0 # check if target name already exists. if [[ -f "''${new_name}" ]]; then _error "''${new_name}: Already exists!" return 1 fi # rename target. mv -- "''${target}" "''${new_name}" && echo "''${new_name}" } _iterate_targets process ''${targets[@]} } # Rename all files to their hashes while keeping extensions. # All files by default. # Usage: name_hash [FILES] function name_hash() { local IFS=$'\n' local targets=(''${@}) [[ "''${targets}" = "" ]] && targets=($(_ls_file)) process() { # extract extension. local extension="''${target##*.}" if [[ "''${extension}" = "''${target}" ]]; then extension="" else extension=".''${extension}" fi # hash the new name. local hash=$(pv "''${target}" | sha1sum | cut -d\ -f1) new_name="''${hash,,}''${extension,,}" # check if same name. [[ "''${target}" = "''${new_name}" ]] && return 0 # rename target. mv -- ''${target} ''${new_name} && echo ''${new_name} } _iterate_targets process ''${targets[@]} } # Check hashes for previously renamed files. # All files by default. # Usage: name_hash_check [FILES] function name_hash_check() { local IFS=$'\n' local targets=(''${@}) [[ "''${targets}" = "" ]] && targets=([^.]*) process() { # extract hashes. local stored="''${target%%.*}" local actual=$(pv "''${target}" | sha1sum | cut -d\ -f1) # compare hashes. if [[ "''${stored}" != "''${actual}" ]]; then _error "Failed." return 1 fi } _iterate_targets process ''${targets[@]} } # Rename files for Jellyfin shows, i.e. `Episode S01E01.mkv` # All files by default. # Usage: name_show [FILES] function name_show() { local IFS=$'\n' local season="$(realpath .)"; season="''${season##*\ }" local episode=0 local targets=(''${@}) [[ "''${targets}" = "" ]] && targets=($(_ls_file)) # Error when no season number specified. if [[ "''${season}" = "" ]]; then _error "Could not determine season number." return 2 fi process() { ((episode++)) # extract new name. local new_name="Episode S''${season}E$(printf %02d ''${episode}).''${target##*.}" # Skip on no change. [[ "''${target}" = "''${new_name}" ]] && return 0 # rename target. mv -- ''${target} ''${new_name} && echo ''${new_name} } _iterate_targets process ''${targets[@]} } # Rename files for Kavita manga format. # All files by default. # Usage: name_manga [FILES] function name_manga() { local IFS=$'\n' local manga=''${PWD##*/} local season=''${1} local episode=0 local targets=(''${@:2}) [[ "''${targets}" = "" ]] && targets=($(_ls_file)) # Error when no season number specified. if [[ "''${season}" = "" ]]; then help name_manga return 2 fi process() { ((episode++)) # Extract new name. local new_name="''${manga} Vol.''${season} Ch.''${episode}.''${target##*.}" # Skip on no change. [[ "''${target}" = "''${new_name}" ]] && return 0 # Rename target. mv -- ''${target} ''${new_name} && echo ''${new_name} } _iterate_targets process ''${targets[@]} } # Rename files for ffmpeg_music_meta format. # All files by default. # Usage: name_music [FILES] function name_music() { local IFS=$'\n' local targets=(''${@}) [[ "''${targets}" = "" ]] && targets=($(ls)) process() { # Extract new name. local ext=''${target##*.} if [[ -d "''${target}" ]]; then local new_name="$(parse_titlecase $(parse_simple ''${target%.*}))" else local new_name="$(parse_titlecase $(parse_simple ''${target%.*})).''${ext}" fi # Skip on no change. [[ "''${target%/}" = "''${new_name}" ]] && return 0 # Rename target. mv -- ''${target} ''${new_name} && echo ''${new_name} } _iterate_targets process ''${targets[@]} } # Rename files with new extension. # All files by default. # Usage: name_ext [FILES] function name_ext() { local IFS=$'\n' local extension=''${1} local targets=(''${@:2}) [[ "''${targets}" = "" ]] && targets=($(_ls_file)) # Error when no new extension specified. if [[ "''${extension}" = "" ]]; then help name_ext return 2 fi process() { # Extract new name. local new_name="''${target%.*}"."''${extension}" # Skip on no change. [[ "''${target}" = "''${new_name}" ]] && return 0 # Rename target. mv -- ''${target} ''${new_name} && echo ''${new_name} } _iterate_targets process ''${targets[@]} } # Change file name prefix. # All matching files by default. # Usage: name_prefix [FILES] function name_prefix() { local IFS=$'\n' local old=''${1} local new=''${2} local targets=(''${@:3}) [[ "''${targets}" = "" ]] && targets=(''${old}*) process() { # Create new name. local new_name="''${new}''${target#$old}" # Skip on no change. [[ "''${target}" = "''${new_name}" ]] && return 0 # Rename. mv -- ''${target} ''${new_name} && echo ''${new_name} } _iterate_targets process ''${targets[@]} } # Change file name postfix. # All matching files by default. # Usage: name_postfix [FILES] function name_postfix() { local IFS=$'\n' local old=''${1} local new=''${2} local targets=(''${@:3}) [[ "''${targets}" = "" ]] && targets=(*''${old}) process() { # Create new name. local new_name="''${target%$old}''${new}" # Skip on no change. [[ "''${target}" = "''${new_name}" ]] && return 0 # Rename. mv -- ''${target} ''${new_name} && echo ''${new_name} } _iterate_targets process ''${targets[@]} } # Replace part of the name. # All matching files by default. # Usage: name_replace [FILES] function name_replace() { local IFS=$'\n' local old=''${1} local new=''${2} local targets=(''${@:3}) [[ "''${targets}" = "" ]] && targets=(*''${old}*) process() { # Create new name. local new_name="''${target//$old/$new}" # Skip on no change. [[ "''${target}" = "''${new_name}" ]] && return 0 # Rename. mv -- ''${target} ''${new_name} && echo ''${new_name} } _iterate_targets process ''${targets[@]} } # Fix numbering for numbered files. I.e if there are 10 items and some of them start without zero, then append zero to it. 1..10 -> 01..10. # Usage: name_fix_numbering [FILES] function name_fix_numbering() { local IFS=$'\n' local highest=0 local power=0 local targets=(''${@}) [[ "''${targets}" = "" ]] && targets=($(ls | grep "^[0-9]")) # Count leading zeroes. for target in "''${targets[@]}"; do # Check that starts with a digit. [[ "''${target}" =~ ^[0-9] ]] || continue local digits=($(parse_ints "''${target}")) local digit="''${digits[0]}" digit=$((10#''${digit})) [[ "''${digit}" -gt "''${highest}" ]] && highest="''${digit}" done local i=''${highest} while [[ i -gt 0 ]]; do ((power++)) i=$((''${i}/10)) done process() { # Check that starts with a digit. if [[ ! "''${target}" =~ ^[0-9] ]]; then _error "Does not start with a digit!" return 1 fi # Prepare new name. local digits=($(parse_ints "''${target}")) local digit="''${digits[0]}" digit=$((10#''${digit})) local new_name=$(printf "%0''${power}d" "''${digit}")"''${target#''${digits[0]}}" # Skip if the same name. [[ "''${target}" = "''${new_name}" ]] && return 0 # Check that file does not exist. if [[ -e "''${new_name}" ]]; then _error "''${new_name}: File exists!" return 1 fi mv -- ''${target} ''${new_name} && echo ''${new_name} } _iterate_targets process ''${targets[@]} } function _comp_name_parse() { _autocomplete_first_ls $(find_function | grep ^parse) } complete -o filenames -F _comp_name_parse name_parse ''; }