I needed a way to move files off drives on the array without needing to give Unbalance exclusive access and wasn't able to find a good solution. So I wrote this script; I'm not sure if there's a better place to post it but I'm posting it here. All the obvious warnings apply (and are re-iterated in the comments at the top of the script). In particular, there is a reason Unbalance requires exclusive access. I've done the bare minimum by adding "system" and "appdata" to the ignored shares/directories, but this is not a "safe" operation.
#!/bin/bash
# source (array) and external drive directories
ARRAY_DIR="/mnt/user" # should be the same for all unraid installations, I think
EXTERNAL_DIR="/mnt/disks/YOUR_EXT_DRIVE" # I used Unassigned Devices to create a mount point
# disks within the array that you want to clear; files paths relative to these
# must be the same as that file's path relative to ARRAY_DIR
PATHS_TO_CLEAR=("/mnt/disk1" "/mnt/disk3") # disk(s) you want to clear
IGNORE_DIRS=("system" "appdata") # these directories/shares will not be processed
# minimum file size in bytes (files smaller than this will not be processed)
# MIN_SIZE=$((100 * 1024 * 1024)) # 100MB
MIN_SIZE=0 # no minimum size
# OVERVIEW
# The purpose of this script is to get files off one or more drives without exclusive access (a la
# Unbalance). Start by excluding the disk(s) you plan to remove from all shares, set the variables
# above, then run the script (either directly or with User Scripts plugin). The script will copy
# files one by one to the non-array directory, then back onto the array into the correct drives,
# finally deleting it from the source drive(s) to be removed.
# WARNING: When this script runs, if the copy from the external drive is successful, the version on
# array will be deleted (replaced by the version copied to the external drive). So don't use this
# script on a drive that contains files that change frequently and could cause problems if overwritten
# such as configuration files.
# WARNING: This script will skip past files that are currently in use (as evidenced by not being able
# to delete them from the array). Try not to use this script on files that will be in use; it won't
# break the script, but it might result in unpredictable behavior.
# WARNING: Only tested once on 6.12.4. Use at your own risk.
copy_and_replace() {
local disk_file="$1"
local external_file="$2"
local array_file="$3"
local temp_array_file="${array_file}.new"
echo "Processing file: $disk_file"
# check if file already exists on the external drive
if [ -e "$external_file" ]; then
disk_size=$(stat -c %s "$disk_file")
external_size=$(stat -c %s "$external_file")
# if sizes match, assume file has already been copied
if [ "$disk_size" -eq "$external_size" ]; then
echo "File already exists on external drive with matching size. Skipping copy to external drive."
else
echo "File exists on external drive but sizes differ. Overwriting..."
cp "$disk_file" "$external_file"
fi
else
echo "Copying $disk_file to external location $external_file..."
cp "$disk_file" "$external_file"
fi
# copy file back to the array with a ".new" extension
echo "Copying to array at temporary location $temp_array_file..."
cp "$external_file" "$temp_array_file"
# check if copy back to array was successful
if [ $? -eq 0 ]; then
echo "Copy back to array successful. Replacing original file in array and deleting from external drive..."
mv "$temp_array_file" "$array_file"
rm -f "$external_file"
else
echo "Error: Could not copy $external_file back to $array_file"
fi
}
# generate find command's prune parameters for ignored directories
PRUNE_CMD=""
for dir in "${IGNORE_DIRS[@]}"; do
PRUNE_CMD+=" -o -path '$dir' -prune "
done
# Iterate through all specified disks within the array
for disk_path in "${PATHS_TO_CLEAR[@]}"; do
echo "Starting the file transfer process for $disk_path..."
# Iterate through all files in the current disk directory
eval "find '$disk_path' -type d \( -false $PRUNE_CMD \) -o -type f -size +'${MIN_SIZE}c' -print0" | while IFS= read -r -d '' disk_file; do
# calculate relative path and corresponding external and array file paths
relative_path="${disk_file#$disk_path/}"
external_file="$EXTERNAL_DIR/$relative_path"
array_file="$ARRAY_DIR/$relative_path"
# create external directory if it doesn't exist
mkdir -p "$(dirname "$external_file")"
copy_and_replace "$disk_file" "$external_file" "$array_file"
done
done
echo "File transfer process completed."