#!/bin/bash # /usr/local/bin/name_tidy # https://crystalfaeries.net/posix/bin/name_tidy # celeste:crystalfaery NAME_TIDY 2020-08-26 19:31:51+00:00 # 2012-06-28 10:23:31+00:00 This only handles $1, not $2-$n so please rewrite it soon :-) # We tidy the names of our arguments, their contents, or current directory # We SHOULD accept files as arguments versus demanding a whole directory # We presume to do the current directory or the one named as argument (after optional recursion flag -r) # 2011-10-08 19:14:20+00:00 had to hack the file renaming logic to accomodate the NTFS handling of case # 2011-11-14 00:39:48+00:00 fixing bug with rsyncing an empty non cannonically named dir over its empty cannonically named empty dir. # 2013-10-03 18:20:15+00:00 use "set -P" to avoid following symlinks set -P # do not follow symlinks (####### THIS DOES NOT WORK! WE DO FOLLOW SYMLINKS! #######) # ONLY UPON ACTIVATION OF A SWITCH OPTION SHOULD WE FOLLOW SYMLINKS BUG! # 2014-03-18 10:05:15+00:00 filter default directory files {HEADER,README}.{html,txt} # 2015-08-02 11:19:52+00:00 we noticed we failed on a directory named "_". # 2015-10-04 18:29:57+00:00 we are not yet handling names beginning with a "-". (hack added at end) # 2016-07-27 11:46:53+00:00 we finally handle a Run_Command_file per directory to block name_tidy operations there! # (echo "no" > .name_tidy) will block name_tidy in cwd. # future option? (echo "norecurse" > .name_tidy) could block recursion into subdirs? # streamripper generates " - .mp3" for a stream lacking metadata # Is this first execution for this user? We must default to safety... if [ \! -r $HOME/.name_tidy_exclude.txt ] then # generate a list of system directories to NEVER name_tidy: if [ -d /usr/share/rear/skel/default/ ] then cd /usr/share/rear/skel/default/ && \ for d in $(find . -mindepth 1 -maxdepth 1 -type d | sort | sed 's/^\.//') do echo $d >> /tmp/exclude.$$.txt done fi pushd / 1>/dev/null 2>/dev/null for d in $(find . -mindepth 1 -maxdepth 1 -type d | sort | sed 's/^\.//') do echo $d >> /tmp/exclude.$$.txt done popd 1>/dev/null 2>/dev/null sort -u /tmp/exclude.$$.txt > $HOME/.name_tidy_exclude.txt \ && rm /tmp/exclude.$$.txt \ || exit $? # Fail Safe fi name_tidy_exclude="$HOME/.name_tidy_exclude.txt" # System-Wide Exclude List for Executing User let recurse=1 # false by default if [ "${1}" ] # any arguments on command line? then if [ "${1}" == "-r" ] # yes, "-r" tells us to recurse subdirectories then let recurse=0 # true by option shift # finished option fi fi if [ "${1}" ] # any target directory arguments on command line? then if [ -d "${1}" ] # other arguments should be directories to process then cd -P "${1}" || exit -1 # make next directory to process be current working directory shift # finished with argument else if [ -h "${1}" ] # other arguments could be symlinks to directories to process then cd -P "${1}" || exit -1 # make next directory to process be current working directory shift # finished with argument else echo "$0: ERROR (neither dir nor symlink): ${1}." 1>&2 exit -1 # better to die then do the wrong thing fi fi else cd -P . # force physical location fi directory="`pwd`" # our physical location # is this directory excluded by our user's system-wide exclude list? if [ -r "${name_tidy_exclude}" ] then # we have an exclude list if [ grep "${directory}" "${name_tidy_exclude}" >& /dev/null ] then # we are NOT to name_tidy this directory (nor its subdirectories unless invoked below this level) echo "" 1>&2 echo "NOT tidying $directory due to global Exclude file" 1>&2 exit 0 # gracefully decline to work here fi fi # does this directory contain a "run control" file denying our tidy operation here? if [ -r .name_tidy ] then # we have a "run control" file applicable to this directory RC="`head -n 1 .name_tidy`" # as simple as we can make it (for now) if [ "${RC}" = "no" ] then # we are NOT to name_tidy this directory (nor its subdirectories unless invoked below this level) echo "" 1>&2 echo "NOT tidying $directory due to local Run Control file" 1>&2 exit 0 # gracefully decline to work here else echo " tidying $directory" 1>&2 fi fi # start by expunging recursive directory hierarchies... callously discarding contents of insane structures... no_recurse # follow the White Rabbit, Neo (DANGER! DANGER!, Will Robinson) let loop=1 # start the loop while [[ $loop -gt 0 ]] ; do let loop=0 # end the loop when we make no changes in a pass # start by fixing names that look like options to commands: for f in $(find . -maxdepth 1 -name '-*' 2>/dev/null | sed 's/^\.\/-*//g') do # iterate nice ionice -c 3 rsync -auvzH ./-*"${f}" "${f}" && rm -rf ./-*"${f}" || echo "$0:${directory}/-*${f}" 1>&2 done mv -i " - .mp3" $(now -t).mp3 2>/dev/null # StreamRipper creates these when lacking metadata in stream (KKCR) for i in *; do # iterate through each item in current working directory # Files are renamed, though .* files are untouched if [ "$i" == "gPodder" ] # gPodder database then continue # leave gPodder files alone elif [ "$i" == "CACHEDIR.TAG" ] # see /var/cache/CACHEDIR.TAG then continue # leave CACHEDIR.TAG files alone elif [ "$i" == "HEADER.txt" ] # default directory files then continue # leave HEADER.html files alone elif [ "$i" == "HEADER.html" ] # default directory files then continue # leave HEADER.html files alone elif [ "$i" == "README.html" ] # default directory files then continue # leave README.html files alone elif [ "$i" == "README.txt" ] # default directory files then continue # leave README.html files alone elif [ "$i" == "README" ] # default directory files then continue # leave README files alone elif [ "$i" == "Incomplete Downloads" ] # do not interfere with downloads currently in progress then continue # leave Incomplete Downloads directory alone elif [ "$i" == "Converted" ] # do not alter our Converted folder name then continue # leave Converted directory alone elif [ "$i" == "Downloads" ] # do not alter our Downloads then continue # leave downloads directory alone elif [ "$i" == "Desktop" ] # do not alter our Desktop directory then continue # leave downloads directory alone elif [ "$i" == "Mail" ] # do not alter our e-Mail directory then continue # leave downloads directory alone elif [ "$i" == "Screencasts" ] # do not alter our Screencasts directory then continue # leave downloads directory alone elif [ "$i" == "*.part" ] # do not interfere with downloads currently in progress then continue # do not interrupt downloads currently in progress fi if [ -a "$i" ] then # Accesible OLDNAME="$i" NEWNAME=$(echo "$i" | tr 'A-Z ' 'a-z_' | sed 's/"/_/g' | sed "s/'/_/g" | sed 's/`/_/g' | sed 's/(/_/g ; s/)/_/g ; s/_-_/-/g ; s/\.-\./-/g ; s/-_-/-/g ; s/-\.-/-/g ; s/^_//g ; s/__/_/g ; s/,/-/g ; s/!/./g ; s/\.\././g ; s/\.-/./g ; s/-_/-/g ; s/_-/-/g ; s/\[/_/g ; s/\]/_/g ; s/#/./g ; s/&/+/g ; s/^\.//g ; s/:/./g ; s/%20/_/g') if [ "$NEWNAME" == "" ] then NEWNAME="_" fi if [ "$OLDNAME" == "_" ] then NEWNAME="_" fi if [ "$NEWNAME" != "$OLDNAME" ] then let loop+=1 # we had to rename something and should loop because further tidy may be necessary # echo "" 1>&2 # a blank line between files renamed for easy reading if [ -d "$NEWNAME" ] then # we have at least one directory involved if [ -d "$OLDNAME" ] then # rsync the directories rmdir "$OLDNAME" if [ $? -ne 0 ] then nice ionice -c 3 rsync -auvzH ./"$OLDNAME"/.??* "$NEWNAME" && rm -rf ./"$OLDNAME"/.??* 2>/dev/null nice ionice -c 3 rsync -auvzH ./"$OLDNAME"/* "$NEWNAME" && rm -rf ./"$OLDNAME" fi else # file vs directory echo "In `pwd` I want to rename the file "$OLDNAME" over the "$NEWNAME" directory, which would be bad, so you fix it manually..." 1>&2 echo "...it could be a collision of a directory and a symbolic link... in which case edit $0" 1>&2 exit -2 fi else # good, no directory in the way if [ -f "$NEWNAME" ] # do we already have a file by this name? then # filename collision, let's update if [ "$NEWNAME" == "$(echo "$OLDNAME" | tr '[:upper:]' '[:lower:]')" ] then # EEEK, name match requires special handling on NTFS mv ./"$OLDNAME" ./"$OLDNAME"- nice ionice -c 3 rsync -auvzH ./"$OLDNAME"- "$NEWNAME" && rm -rf ./"$OLDNAME"- else # Phew, no need to dance around Bill Gates steaming pile of excrement nice ionice -c 3 rsync -auvzH ./"$OLDNAME" "$NEWNAME" && rm -rf ./"$OLDNAME" fi else mv -v -- ./"$OLDNAME" "$NEWNAME" # no filename collision, just rename fi fi fi # Directories are recursed if we were invoked with option -r if [ $recurse = 0 ] then # we were commanded to recurse if [ -d "$NEWNAME" ] then # directory "$0" -r "$NEWNAME" # Follow the White Rabbit, Neo. fi fi # This code stupidly preserves the (possibly) older version rather then overwriting it with (possibly) newer version # We would prefer to rsync the new toxic name over the old detoxed name (doing what detox itself SHOULD do (at least optionally)) # followed by removal of the toxic name, but we're skipping the rsync step now, # But it will take some thought to implement the improved version doing so... # Even better but even more work, would be to write a shell script wrapper around detox itself to fix it, # as I'm not about to go hack detox code in another language. # for f in $(/usr/bin/detox ${NEWNAME} |& grep "Cannot rename" | sed 's/^Cannot rename //;s/ .*$//'|sort -u) # do # if [ -f "${f}" ] # then # echo "DETOXifying ${f}" 1>&2 # rm "${f}" 1>&2 # fi # done fi done # i done # loop until no changes necessary exit 0 # Pau!