Created
January 22, 2024 07:02
-
-
Save Roy-Orbison/be10716f4522c44ae1bf5d6e302915f0 to your computer and use it in GitHub Desktop.
Revisions
-
Roy-Orbison created this gist
Jan 22, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,254 @@ #!/bin/bash set -e #shopt -s compat32 # illegal on windows re_ctrl='[:cntrl:]' re_ctrl="^([^$re_ctrl]*)[$re_ctrl]+(.*)" re_punct='<>:"\\/|?*' re_punct="^([^$re_punct]*)[$re_punct]+(.*)" re_ts_td='[. ]+$' # here be dragons! re_win='^(CO(N|M[1-9])|PRN|AUX|NUL|LPT[1-9])(\.[^.]+)?$' # not illegal but quite problematic re_ls='^ +(.*)' re_cs=' +(.*)' re_tsf=' ((\.[^.[:space:]]+)+)$' punct_replacement='-' startpoint=() list=0 # only used to check for conflicts rename=0 show_new=0 helpme=0 badopts=0 while getopts ':hnlp:r' opt; do case $opt in h) helpme=1 ;; n) show_new=1 ;; l) list=1 ;; p) punct_replacement="$OPTARG" if [[ "$punct_replacement" =~ $re_ctrl || "$punct_replacement" =~ $re_punct ]]; then badopts=1 >&2 echo -n 'Invalid punctuation replacement character: '; >&2 printf '%q\n' "$punct_replacement" fi ;; r) rename=1 ;; :) badopts=1 case $OPTARG in p) >&2 echo "You did not specify any punctuation replacement after the '-$OPTARG' option." ;; *) >&2 echo "Argument missing from option '-$OPTARG'." ;; esac ;; \?) badopts=1 >&2 echo "Unknown option '-$OPTARG'." ;; esac done if (( OPTIND > 1 )); then shift $(( OPTIND - 1 )) fi case $(( list + rename )) in 0) helpme=1 ;; 1) if [[ $# -eq 0 ]]; then badopts=1 >&2 echo You must specify at least one directory or file as a starting point. >&2 echo Use a dot for the current directory. else startpoint=("$@") for path in "${startpoint[@]}"; do if [[ ! -e "$path" ]]; then badopts=1 >&2 printf '%q ' "$path"; >&2 echo is inaccessible or does not exist. fi done fi ;; 2) badopts=1 >&2 echo Can either output a list of files, or rename them, not both. ;; esac if (( badopts + helpme )); then if (( badopts )); then helpout=2 else helpout=1 fi >&$helpout cat <<-EOT Usage: $0 [ -h ] $0 -l [ -p PUNCT ] [ -n ] START_POINT [ START_POINT ... ] $0 -r [ -p PUNCT ] START_POINT [ START_POINT ... ] Options: -h This help text (the default). -l List problematic files. -n Show suggested new names as well. -r Rename problematic files. -p Override dash character used for replacing illegal punctuation. EOT exit $badopts fi if (( rename )); then >&2 echo Will prompt for renames. Ensure you only rename items BELOW the main Dropbox directory, >&2 echo and do not change your account name directory. Press Ctrl + C to cancel. else >&2 echo Will list problematic filenames. >&2 echo 'To perform renames, add the "-r" option before the starting point(s).' >&2 echo fi suggest () { suggested="$1" local original="$suggested" es=0 # order of replacements is significant while [[ "$suggested" =~ $re_ctrl ]]; do suggested="${BASH_REMATCH[1]} ${BASH_REMATCH[2]}" done while [[ "$suggested" =~ $re_punct ]]; do suggested="${BASH_REMATCH[1]}$punct_replacement${BASH_REMATCH[2]}" done if [[ "$suggested" =~ $re_ls ]]; then suggested="${BASH_REMATCH[1]}" fi if [[ "$suggested" =~ $re_ts_td ]]; then before_end=$(( ${#suggested} - ${#BASH_REMATCH[0]} )) suggested="${suggested:0:before_end}" fi while [[ "$suggested" =~ $re_cs ]]; do before_end=$(( ${#suggested} - ${#BASH_REMATCH[0]} )) suggested="${suggested:0:before_end} ${BASH_REMATCH[1]}" done while [[ "$suggested" =~ $re_tsf ]]; do before_end=$(( ${#suggested} - ${#BASH_REMATCH[0]} )) suggested="${suggested:0:before_end}${BASH_REMATCH[1]}" done # "${reserved@Q}" and "${reserved^^}" not avail. reserved="$(printf '%sX\n' "$suggested" | tr '[a-z]' '[A-Z]')" reserved="${reserved%X}" if [[ "$reserved" =~ $re_win ]]; then suggested="_$suggested" es=33 elif [[ -z "$suggested" ]]; then es=22 elif [[ "$suggested" != "$original" ]]; then es=11 fi set +e # don't halt on custom errors return $es } re_skip='[?][[:space:]]*$' skipped=0 while IFS= read -r -d $'\0' path <&3; do if [[ ! -e "$path" && ! -L "$path" ]]; then >&2 echo "'$path' is inaccessible or no longer exists." exit 1 fi parent="$(dirname "$path"; err=$?; echo X; exit $err)" parent="${parent%?X}" current="$(basename "$path"; err=$?; echo X; exit $err)" current="${current%?X}" if [[ "$current" == . || "$current" == .. ]]; then continue fi suggest "$current" suggest_error=$? set -e if ! (( suggest_error )); then # already compliant continue fi if ! (( rename )); then if (( show_new )); then echo "$path"$'\t'"$parent/$suggested" else echo "$path" fi continue fi first_suggested="$suggested" while : ; do >&2 echo if [[ "$parent" == . ]]; then in= else in=" in '$parent'" fi if (( suggest_error == 33 )); then >&2 echo "'$reserved' is a reserved filename on Windows, so can never be used." fi >&2 echo "Confirm or adjust sanitised name for '$current'$in." >&2 echo "To skip past this rename, type a ? at the end." read -r -e -i "$suggested" -p 'New name: ' confirmed="$REPLY" if [[ "$confirmed" =~ $re_skip ]]; then (( skipped++ )) || true >&2 echo Skipping. break fi suggest "$confirmed" suggest_error=$? set -e case $suggest_error in 0) path_new="$parent/$suggested" if [[ -e "$path_new" ]]; then >&2 echo Something with that name already exists. else mv "$path" "$path_new" break fi ;; 22) if [[ "$confirmed" != "$suggested" ]]; then >&2 echo -n 'That name becomes zero length after re-sanitising it. ' fi >&2 echo Names cannot be zero length. suggested="$first_suggested" ;; *) >&2 echo That adjusted name still does not comply. ;; esac done done 3< <(find -H "${startpoint[@]}" -depth -print0) symlinks_broke=0 if (( rename )); then >&2 echo while IFS= read -r -d $'\0' symlink <&3; do (( symlinks_broke++ )) || true >&2 echo "The target of the symlink '$symlink' no longer exists." done 3< <(find -H "${startpoint[@]}" -depth -xtype l -print0) if (( symlinks_broke )); then >&2 echo fi >&2 echo Done. fi exit $(( ( skipped + symlinks_broke ) > 0 ))