Find and shorten file and folder names with a certain length

Recently, I restructured my Synology NAS and encrypted all backup shared folders using AES-256 encryption. Therefore I had to find and shorten all files and folders with a certain character length.

In general the activation of shared folder encryption is straightforward:

  • Navigate to the desired folder in the Synology DSM control panel (see image below)
  • Activate folder encryption
  • Create a strong passphrase
  • Download the generated encryption key file for safekeeping

Once encrypted, your data will be protected.

However, there is a limitation on the maximum name length for encrypted files and folders.

“The name of a file or a folder in an encrypted shared folder can’t exceed 143 English or 47 Asian (CJK) characters. On macOS, it can’t exceed 130 English or 43 Asian (CJK) characters.” (Source: Synology Knowledge Center)

Unfortunately, some files in my old Active Backup for Business data backups exceeded this limit. These backups are stored as plain file system copies of the source systems, organized by date.
As a result, I had to find and shorten all files with filenames longer than 130 characters in all version folders.

Shell command to find all files longer than x characters

To find all files with more than x characters in their filename, I used the shell access of my NAS and ran the following one-liner:

$> find /PATH/TO/FOLDER -type f -print0 | while IFS= read -r -d '' file; do basename=$(basename "$file"); if [ ${#basename} -gt 130 ]; then echo "$file"; fi; done

In this example the maximum number of characters is 130. The result is a list of all found files with their relative path (relative to execution directory) in the files system.

A shell script to find and shorten all files with overlength

Hence I’m lazy and do not want to rename all the findings by hand, I simply created a little shell script, that finds and shortens all filenames of all candidates under a given path to a given length.

#! /bin/bash
set -e # Exit immediately if a command exits with a non-zero status

search="."
if [ ! -z "$1" ]; then # If parameter $1 is provided, use it as the search directory
    search="$1"
fi

maxlength=10
if [ ! -z "$2" ]; then # If parameter $2 is provided, use it as the maximum filename length
    maxlength="$2"
fi

scripname=$(basename "$0")

# Find file an folder candidates and sort them by reversed path depth (deeper paths first)
# Because we want to rename child folders and files in folders, that will be renamed, first. 
# Otherwise these children wouldn't be available anymore by old paths after renaming their parent. 
# Hence file paths could include spaces, we use ":" as field separator.
find ${search} -printf "%d:%p\n" | sort -t ':' -rnbk1 | while IFS=: read -r depth path; do
    basename=$(basename "$path")

    # exclude the scriptfile itself an current folder .
    if [[ "${basename}" == "${scripname}" || "${basename}" == "." ]]; then
        return
    fi

    # get plain filename (everything before the first "." -> also works with multi extension files like *.sql.tar.gz)
    plainName=${basename%%.*}
  
    if [[ ${#plainName} -gt ${maxlength} ]]; then # count only the name part without extension
        extension=""

        # only examine extensions if present
        if [[ "$basename" == *.* ]]; then

            # get complete extension by removing the plain filename from complete filename with extension
            extension=".${basename#${plainName}.}"
        fi

        # Truncate filename and trim possible whitespace (xargs -> f.e: if path contaians spaces)
        newName=$(echo "${plainName:0:${maxlength}}" | xargs)

        # add extension and path
        newPath="$(dirname "$path")/$newName$extension"

        mv "$path" "$newPath"
        echo "Renamed: '$path' to '$newPath'"
    fi
done

First this script defines defaults for search path (current directory “.”) and the maximal amount of characters allowed for filenames (here 10). Afterwards it searches for candidates inside the given path (Script parameter $1 or the current folder as default). For each respective candidate the filename is extracted. When a filename exceeds the given maximal length (script parameter $2 or default 10), it is shortened to the greatest allowed length.
Of course, the script should also work for file names without extensions, which are not so rare on Unix systems. That’s why the script checks if there is a file extension and truncates only the filename. Finally the extension is added to the shortened filename again and the original file is renamed.

Example usage to find and shorten all files/folders to 130 chars

$> FindAndShorten.sh /path/to/search/candidates 130

Further information on used commands and structures

Other examples for using find:

Here you can find more information on the find command:

Leave a Reply

Your email address will not be published. Required fields are marked *

 

This site uses Akismet to reduce spam. Learn how your comment data is processed.