Skip to content

Instantly share code, notes, and snippets.

@bwmorales
Last active June 29, 2018 18:10
Show Gist options
  • Save bwmorales/40defa96fdc3331c379f48e0b8acd893 to your computer and use it in GitHub Desktop.
Save bwmorales/40defa96fdc3331c379f48e0b8acd893 to your computer and use it in GitHub Desktop.
Swap out those pesty SMB illegals with benign characters!
#!/bin/bash
# USAGE: ./rmSMB_Illegals.sh path
# Find searches through each file on a filesystem and passes the filenames to a
# function that tests for the presence of illegal characters and remediates.
# Bad characters are either removed or replaced with a ``-''.
# This has been tested on APFS on macOS 10.13.5. It'd be pretty safe to run this
# on a mounted volume, but it'd be nicer to test on Synology Linux and have it
# run directly.
LOG_FILE="$(pwd)/$0.log"
REGEX='^[][a-zA-Z0-9_ .-]*$'
TRAILING_SPACE=' $'
TRAILING_PERIOD='\.$'
writeToLog(){
local LOG_DIRNAME
LOG_DIRNAME=$(dirname "${LOG_FILE}")
if [[ ! -d "${LOG_DIRNAME}" ]]; then
mkdir -p "${LOG_DIRNAME}"
touch "${LOG_FILE}"
printf "%s Log and log parent directory created.\n" \
"$(date "+%Y/%m/%d %H:%M:%S")" >> "${LOG_FILE}"
fi
if [[ ! -e "${LOG_FILE}" ]]; then
touch "$LOG_FILE"
printf "%s Log created.\n" "$(/bin/date "+%Y/%m/%d %H:%M:%S")" \
>> "${LOG_FILE}"
fi
LOGGED_MESSAGE="$*"
printf "%s $LOGGED_MESSAGE\n" "$(/bin/date "+%Y/%m/%d %H:%M:%S")" \
>> "$LOG_FILE"
}
testForIllegalCharacters(){
STRING="$1"
# Check that file name string only contains white-listed characters.
if [[ $STRING =~ $REGEX ]]; then
# Check that white-listed characters are not in bad places
if [[ ! $STRING =~ $TRAILING_SPACE ]]; then
: # No trailing space... moving along
else
return 1 # Trailing space found
fi
if [[ ! $STRING =~ $TRAILING_PERIOD ]]; then
: # Not trailing period found... moving along
else
return 1 # Trailing period found
fi
return 0 # YAY! This file is clean!
else
return 1 # Non-whitelisted character found. We'll take some action.
fi
}
sanitizeFilename(){
STRING="$1"
STRING_REPLACEMENT="$STRING" # Set variable outside of for loop
writeToLog "WORKING: \"$TARGET_FILE\""
# Let's iterate through this string and replace anything not on the whitelist
for (( INDEX = 0; INDEX < ${#STRING}; INDEX++ )); do
CHAR=${STRING:$INDEX:1}
if [[ ! $CHAR =~ $REGEX ]]; then # We found one!
# writeToLog "\t$STRING: '$CHAR' at index $INDEX"
STRING_REPLACEMENT=$(printf $STRING_REPLACEMENT \
| sed "s,.,-,$(expr $INDEX + 1)")
fi
done
if [[ $STRING_REPLACEMENT =~ $TRAILING_SPACE ]]; then # return without trailing space
# writeToLog "\t$STRING_REPLACEMENT: ' ' at index $(expr ${#STRING_REPLACEMENT} - 1)"
printf "$STRING_REPLACEMENT" | sed "s, $,,"
return 0
elif [[ $STRING_REPLACEMENT =~ $TRAILING_PERIOD ]]; then # return without trailing period
# writeToLog "\t$STRING_REPLACEMENT: '.' at index $(expr ${#STRING_REPLACEMENT} - 1)"
printf "$STRING_REPLACEMENT" | sed "s,.$,,"
return 0
else # We're good to go.
printf "$STRING_REPLACEMENT"
return 0
fi
}
if [[ $EUID -ne 0 ]]; then
writeToLog "This must be run as root."
exit 1
fi
OIFS=$IFS
IFS=$'\n'
# find will crawl the filesystem performing some tests on each filename
for TARGET_FILE in $(find "$1" -name "*" | grep -vE "^\.*$"); do
TARGET_BASE="$(basename $TARGET_FILE)"
if [[ $TARGET_BASE == ".DS_Store" ]]; then
rm "$TARGET_FILE"
fi
testForIllegalCharacters "$TARGET_BASE" # Run initial test. Clean returns 0.
if [[ "$?" -eq "1" ]]; then # We found a dirty filename
# Find a decent, sanitized name for the file.
SANITIZED_NAME="$(sanitizeFilename "$TARGET_BASE")"
# Let's make sure we don't overwrite anything.
if [[ -e "$(dirname $TARGET_FILE)/$SANITIZED_NAME" ]]; then
# The sanitized name would overwrite something, so we'll add the date
# to the beginning of the filename.
mv "$TARGET_FILE" \
"$(dirname $TARGET_FILE)/$(date +%Y-%m-%d)_$SANITIZED_NAME"
writeToLog "\t$(dirname $TARGET_FILE)/$SANITIZED_NAME already exists!"
writeToLog "CHANGE: $TARGET_FILE to $(dirname $TARGET_FILE)/$(date +%Y-%m-%d)_$SANITIZED_NAME"
else
# No conflict. Going to go ahead and overwrite.
mv "$TARGET_FILE" "$(dirname $TARGET_FILE)/$SANITIZED_NAME"
writeToLog "CHANGE: $TARGET_FILE to $(dirname $TARGET_FILE)/$SANITIZED_NAME"
fi
fi
done
IFS=$OIFS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment