#!/usr/bin/env bash
help_text="
NAME
tidy - Tidies filenames and fixes issues such as permission issues with files in the specified directory.
USAGE
tidy [<folder>]
OPTIONS
-n|--name
Only tidy filenames - do not check permissions etc.
-fn|-nf
Force rename of filenames without prompting. Implies -n.
-h|--help
Show help text.
DESCRIPTION
Tidies filenames and fixes issues such as permission issues with files in the specified directory.
AUTHORS
mjnurse.github.io - 2020
"
help_line="Tidies filenames and fixes issues such as permission issues with files in the specified directory"
web_desc_line="Tidies filenames and fixes issues such as permission issues with files in the specified directory"
force=n
names_only=n
while [[ "$1" != "" ]]; do
case $1 in
-h|--help)
echo "$help_text"
exit
;;
-n|--name)
names_only=y
;;
-fn|-nf)
force=y
names_only=y
;;
-*)
echo "Unknown option: $1"
echo "Try ${0##*/} -h for more information"
exit 1
;;
?*)
cd $1
;;
esac
shift
done
cdef="\e[39m" # default colour
cbla="\e[30m"; cgra="\e[90m"; clgra="\e[37m"; cwhi="\e[97m"
cred="\e[31m"; cgre="\e[32m"; cyel="\e[33m"
cblu="\e[34m"; cmag="\e[35m"; ccya="\e[36m";
clred="\e[91m"; clgre="\e[92m"; clyel="\e[93m"
clblu="\e[94m"; clmag="\e[95m"; clcya="\e[96m"
function cecho {
color=c$1
shift
message="$*"
echo -e "${!color}${message}${cdef}"
}
cecho gre ================================================================================
cecho gre "File Type Shouldn't be executable"
cecho gre ================================================================================
remove_all=n
for f in *; do
if [[ -x "$f" ]]; then
ft="$(file -b "$f")"
case "${ft^^}" in
DIRECTORY*) ft="Directory";;
HTML*) ft="HTML";;
JPEG*) ft="JPEG";;
PNG*) ft="PNG";;
GIF*) ft="GIF";;
JSON*) ft="JSON";;
M*EXCEL*) ft="Excel";;
M*WORD*) ft="Word";;
M*POWERPOINT*) ft="PowerPoint";;
PEM*) ft="PEM";;
PDF*) ft="PDF";;
SVG*) ft="SVG";;
ZIP*) ft="Zip";;
# The following can be executable so skip them
BOURNE*) ft="skip";;
PYTHON*) ft="skip";;
# Print other file types
*) echo $f - $ft;ft="Other";;
esac
if [[ "$ft" != "Other" && "$ft" != "skip" && "$ft" != "Directory" ]]; then
echo --------------------------------------------------------------------------------
echo -e "FILE: $f"
echo -e "TYPE: $ft"
echo --------------------------------------------------------------------------------
opt=""
if [[ "$remove_all" == "y" ]]; then
chmod -x "$f"
continue
fi
while [[ ! "${opt^}" =~ ^[AIQRS]$ ]]; do
[[ -n "$opt" ]] && echo "Invalid option: $opt"
printf "${clblu}[i]gnore rule, [r]emove exec, remove exec from [a]ll, [s]kip, q[uit]. [aiqrS]?: ${cdef}"
read opt
[[ -z "$opt" ]] && opt="S"
done
case $opt in
a|A)
remove_all=y
chmod -x "$f"
;;
r|R)
chmod -x "$f"
;;
i|I)
break;;
q|Q)
exit;;
esac
fi
fi
done
cecho gre ================================================================================
cecho gre "Filename contains character other than [a-z][0-9]_-."
cecho gre ================================================================================
rename_all=n
for f in *; do
if [[ -f "$f" && ! -L "$f" ]]; then
if [[ -x "$f" ]]; then
link_char="-"
exec_message="${cgre}(Executable)${cdef}"
else
link_char="_"
exec_message=""
fi
newname=$(echo "$f" | sed "
s/[^a-zA-Z0-9._-]/${link_char}/g;
s/\([a-z]\)\([A-Z]\)/\1-\L\2/g;
s/\(.*[a-z].*[A-Z].*\)\(\.[^\.]\+\)/\L\1\2/g;
s/\(.*[A-Z].*[a-z].*\)\(\.[^\.]\+\)/\L\1\2/g;
s/\([^\.]*[A-Z][^\.]*[a-z][^\.]*\)/\L\1/g;
s/\([^\.]*[a-z][^\.]*[A-Z][^\.]*\)/\L\1/g;
s/[-_][-_]*/${link_char}/g;
")
if [[ "$f" != "$newname" && "$f" != "README.md" ]]; then
echo --------------------------------------------------------------------------------
echo -e "FILE: $f $exec_message"
echo "Rename as: $newname"
echo --------------------------------------------------------------------------------
if [[ $force == y || $rename_all == y ]]; then
opt="r"
else
opt=""
while [[ ! "${opt^}" =~ ^[AIQRS]$ ]]; do
[[ -n "$opt" ]] && echo "Invalid option: $opt"
printf "${clblu}[i]gnore rule, [q]uit, [r]ename, rename [a]ll, [s]kip [aiqrS]?: ${cdef}"
read opt
[[ -z "$opt" ]] && opt="S"
if [[ "${opt^}" == "A" ]]; then
rename_all=y
opt="r"
fi
done
fi
case $opt in
r|R)
# rename via a temp tile name to avoid the Windows file system saying
# a.txt is same as A.txt etc.
val=$RANDOM
mv "$f" "${newname}.tmp.${val}"
mv "${newname}.tmp.${val}" "$newname"
cecho gre "Renamed to: $newname"
;;
i|I)
break;;
q|Q)
exit;;
esac
fi
fi
done
if [[ $names_only == y ]]; then
exit
fi
cecho gre ================================================================================
cecho gre Executable but not '#!/usr/bin/env ...'
cecho gre ================================================================================
for f in $(find . -maxdepth 1 -executable); do
if [[ -f $f && ! -L $f ]]; then
line1="$(head -1 "$f")"
if [[ "${line1:0:14}" != "#!/usr/bin/env" ]]; then
echo --------------------------------------------------------------------------------
echo FILE: $f
echo --------------------------------------------------------------------------------
echo First 5 lines:
echo --------------
head -5 "$f" | trunc
echo
printf "${clblu}[i]gnore rule, [r]emove exec, add [b]ash, add [p]ython3, [s]kip, q[uit]. [irbpSq]?: ${cdef}"
read opt
case $opt in
r|R)
chmod a-x "$f"
;;
b|B)
sed -i '/^#!\/bin\/bash/d' "$f"
sed -i '1i#!/usr/bin/env bash' "$f"
;;
p|P)
sed -i '1i#!/usr/bin/env python3' "$f"
;;
i|I)
break
;;
q|Q)
exit
;;
esac
fi
fi
done
cecho gre ================================================================================
cecho gre '#!/bin/bash not #!/usr/bin/env bash'
cecho gre ================================================================================
for f in $(find . -maxdepth 1 ! -executable); do
if [[ -f $f && ! -L $f ]]; then
line1="$(head -1 "$f" | tr -d '\000')"
line1=${line1//[$' \t\r\n']}
if [[ "$line1" == "#!/bin/bash" ]]; then
echo --------------------------------------------------------------------------------
echo '#!/bin/bash not #!/usr/bin/env bash'
echo
echo FILE: $f
echo --------------------------------------------------------------------------------
read -p "[i]gnore rule, [u]pdate to #!/usr/bin/env bash, [s]kip, q[uit]. [iuSq]?: " opt
case $opt in
u|U)
sed -i '1s|#!/bin/bash|#!/usr/bin/env bash|' "$f";;
i|I)
break;;
q|Q)
exit;;
esac
fi
fi
done
cecho gre ================================================================================
cecho gre '#!/usr/bin/env bash' but not Executable
cecho gre ================================================================================
for f in $(find . -maxdepth 1 ! -executable); do
if [[ -f $f && ! -L $f ]]; then
line1="$(head -1 "$f" | tr -d '\000')"
if [[ "$line1" == "#!/usr/bin/env bash" ]]; then
echo --------------------------------------------------------------------------------
echo FILE: $f
echo --------------------------------------------------------------------------------
echo First 5 lines:
echo --------------
head -5 "$f"
echo
read -p "[i]gnore rule, [d]elete '#!/usr/bin/env bash', [m]ake exec, [s]kip, q[uit]. [idmSq]?: " opt
case $opt in
d|D)
sed -i '1d' "$f";;
m|M)
chmod u+x "$f";;
i|I)
break;;
q|Q)
exit;;
esac
fi
fi
done
cecho gre ================================================================================
cecho gre Writable by Other
cecho gre ================================================================================
for f in $(find . -maxdepth 1 -perm -o=w); do
if [[ -f $f && ! -L $f ]]; then
echo --------------------------------------------------------------------------------
ls -al $f
echo
read -p "[i]gnore rule, [r]remove writeable other, [s]kip, q[uit]. [irSq]?: " opt
case $opt in
r|R)
chmod o-w $f;;
i|I)
break;;
q|Q)
exit;;
esac
fi
done
cecho gre ================================================================================
cecho gre Writable by Group
cecho gre ================================================================================
for f in $(find . -maxdepth 1 -perm -g=w); do
if [[ -f $f && ! -L $f ]]; then
echo --------------------------------------------------------------------------------
ls -al $f
echo
read -p "[i]gnore rule, [r]remove writeable group, [s]kip, q[uit]. [irSq]?: " opt
case $opt in
r|R)
chmod g-w $f;;
i|I)
break;;
q|Q)
exit;;
esac
fi
done
cecho gre ================================================================================
cecho gre Executable by Other
cecho gre ================================================================================
for f in $(find . -maxdepth 1 -perm -o=x); do
if [[ -f $f && ! -L $f ]]; then
echo --------------------------------------------------------------------------------
ls -al $f
echo
read -p "[i]gnore rule, [r]remove Exec other, [s]kip, q[uit]. [irSq]?: " opt
case $opt in
r|R)
chmod o-x $f;;
i|I)
break;;
q|Q)
exit;;
esac
fi
done
cecho gre ================================================================================
cecho gre Executable by Group
cecho gre ================================================================================
for f in $(find . -maxdepth 1 -perm -g=x); do
if [[ -f $f && ! -L $f ]]; then
echo --------------------------------------------------------------------------------
ls -al $f
echo
read -p "[i]gnore rule, [r]remove Exec group, [s]kip, q[uit]. [irSq]?: " opt
case $opt in
r|R)
chmod g-x $f;;
i|I)
break;;
q|Q)
exit;;
esac
fi
done
cecho gre ================================================================================
cecho gre No header
cecho gre ================================================================================
for f in $(find . -maxdepth 1 -executable); do
if [[ -f $f && ! -L $f ]]; then
l1="$(cat "$f" | sed -n '1p')"
l2="$(cat "$f" | sed -n '2p')"
if [[ "$l1" == "#!/usr/bin/env bash" && "${l2:0:10}" != "help_text=" ]]; then
cecho lcya --------------------------------------------------------------------------------
cecho lcya No header:
ls -al "$f"
cecho lcya --------------------------------------------------------------------------------
head -15 "$f"
echo
read -p "[i]gnore rule, [a]dd header, add [b]asic header, edit [v]im, edit [c]ode, [s]kip, q[uit]. [iabvcSq]?: " opt
case ${opt^} in
A)
tidytmp="/tmp/tidy.tmp"
echo '#!/usr/bin/env bash
help_text="
NAME
'${f:2}' - One line description.
USAGE
'${f:2}' [options] <parameters>
OPTIONS
-x
Description...
-h|--help
Show help text.
DESCRIPTION
Description description description description.
AUTHOR
mjnurse.github.io - 2026
"
help_line="'tbc'"
web_desc_line="'tbc'"
try="Try ${0##*/} -h for more information"
tmp="${help_text##*USAGE}"
usage=$(echo "Usage: ${tmp%%OPTIONS*}" | tr -d "\n" | sed "s/ */ /g")
if [[ "$1" == "" ]]; then
echo "${usage}"
echo "${try}"
exit 1
fi
case $1 in
-h|--help)
echo "$help_text"
exit
;;
esac
' > $tidytmp
tail +2 "$f" >> $tidytmp
cp -f $tidytmp "$f"
;;
B)
tidytmp="/tmp/tidy.tmp"
echo '#!/usr/bin/env bash
help_text="
usage: <<FILENAME>> [options] <filename>
-h : This help text.
"
help_line="tbc"
web_desc_line="tbc"
' > $tidytmp
tail +2 "$f" >> $tidytmp
cp -f $tidytmp "$f"
;;
V)
vgim $f
;;
C)
code $f
;;
I)
break
;;
Q)
exit
;;
esac
fi
fi
done