1 contributor
#!/bin/bash
# Standalone Media Importer
# Version: 1.0
# A comprehensive media file organizer that sorts photos and videos by date
# with various organization patterns and timezone handling
VERSION="1.0"
SCRIPT_NAME="Standalone Media Importer"
# Default values
ORGANIZATION=""
FORCE_FULL_DATE=0
COLLECT_UNSORTABLE=0
SOURCE_PATTERNS=()
DESTINATION=""
KEEP_ORIGINALS=0
DRY_RUN=0
VERBOSE=0
# Counters and statistics
TOTAL_FILES=0
PROCESSED_FILES=0
SKIPPED_FILES=0
ERROR_FILES=0
TOTAL_SIZE=0
PROCESSED_SIZE=0
START_TIME=$(date +%s)
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_color() {
local color="$1"
local message="$2"
echo -e "${color}${message}${NC}"
}
# Function to log messages with timestamp
log_message() {
local message="$1"
local level="${2:-INFO}"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
case "$level" in
"ERROR")
print_color "$RED" "[$timestamp] ERROR: $message" >&2
;;
"WARNING")
print_color "$YELLOW" "[$timestamp] WARNING: $message"
;;
"SUCCESS")
print_color "$GREEN" "[$timestamp] SUCCESS: $message"
;;
"INFO")
if [[ $VERBOSE -eq 1 ]]; then
print_color "$BLUE" "[$timestamp] INFO: $message"
fi
;;
*)
echo "[$timestamp] $message"
;;
esac
}
# Function to display help
show_help() {
cat << EOF
$SCRIPT_NAME v$VERSION
USAGE:
$0 [OPTIONS]
DESCRIPTION:
Organizes media files (photos and videos) by date with various naming patterns.
if [[ $exif_found -eq 0 ]]; then
log_message "Warning: No EXIF date found for $file. Using filesystem modification time."
-o, --organization PATTERN
Organization pattern:
y -> target/yyyy/mm-dd_hh-mm-ss.orig_ext
m -> target/yyyy/mm/dd_hh-mm-ss.orig_ext
d -> target/yyyy/mm/dd/mm-dd_hh-mm-ss.orig_ext
h -> target/yyyy/mm/dd/hh/mm-ss.orig_ext
--full-date
Force all files to be named with full date (yyyy-mm-dd_hh-mm-ss.ext) in the destination folder, regardless of organization pattern.
-s, --source PATTERN
Source folder pattern(s) with simple regex support (*^$)
Can be specified multiple times
Examples:
-s "/DCIM/*Video$"
-s "/path/to/photos"
-s "*.jpg"
Default: all subfolders in current directory except destination
-d, --destination PATH
Destination folder (default: ./sorted)
-k, --keep-originals
Keep original files (copy instead of move)
--dry-run
Show what would be done without actually doing it
-v, --verbose
Enable verbose output
-h, --help
Show this help message
--version
Show version information
EXAMPLES:
# Basic usage - organize all media in current directory
$0
# Organize with monthly folders, keep originals
$0 -o m -k
# Process specific folders with hourly organization
# Return date and source (no warnings or debug output)
echo "$create_date|$date_source"
# Dry run with verbose output
$0 --dry-run -v -s "*.mov" -d "/tmp/test"
DEPENDENCIES:
Required: exiftool
Optional: mediainfo, file (for enhanced metadata detection)
EOF
}
# Function to show version
show_version() {
echo "$SCRIPT_NAME v$VERSION"
echo "A comprehensive media file organizer with timezone support"
}
# Function to check dependencies
check_dependencies() {
local missing_deps=()
# Check for required dependencies
if ! command -v exiftool &> /dev/null; then
missing_deps+=("exiftool")
fi
# Check for optional dependencies
local optional_missing=()
if ! command -v mediainfo &> /dev/null; then
optional_missing+=("mediainfo")
fi
if ! command -v file &> /dev/null; then
optional_missing+=("file")
fi
if [[ ${#missing_deps[@]} -gt 0 ]]; then
print_color "$RED" "ERROR: Missing required dependencies:"
for dep in "${missing_deps[@]}"; do
echo " - $dep"
done
echo ""
echo "Installation instructions:"
echo " macOS: brew install exiftool"
echo " Ubuntu/Debian: sudo apt-get install libimage-exiftool-perl"
echo " CentOS/RHEL: sudo yum install perl-Image-ExifTool"
echo " Arch: sudo pacman -S perl-image-exiftool"
exit 1
fi
if [[ ${#optional_missing[@]} -gt 0 && $VERBOSE -eq 1 ]]; then
log_message "Optional dependencies not found (functionality may be limited): ${optional_missing[*]}" "WARNING"
fi
log_message "All required dependencies found" "SUCCESS"
}
# Function to get file size in bytes
get_file_size() {
local file="$1"
if [[ -f "$file" ]]; then
if command -v stat &> /dev/null; then
# Try GNU stat first (Linux)
stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null
else
# Fallback to ls
ls -l "$file" | awk '{print $5}'
fi
else
echo "0"
fi
}
# Function to format file size
format_size() {
local size=$1
if (( size < 1024 )); then
echo "${size}B"
elif (( size < 1048576 )); then
echo "$(( size / 1024 ))KB"
elif (( size < 1073741824 )); then
echo "$(( size / 1048576 ))MB"
else
echo "$(( size / 1073741824 ))GB"
fi
}
# Function to extract date from file
extract_file_date() {
local file="$1"
local create_date=""
local date_source=""
local exif_found=0
# Try to get creation date from EXIF data
local exif_output=$(exiftool -G1 -s -CreateDate -DateTimeOriginal -DateTime "$file" 2>/dev/null)
if [[ -n "$exif_output" ]]; then
# Parse the exiftool output to find the best date
while IFS= read -r line; do
if [[ "$line" =~ ^\[([^\]]+)\][[:space:]]*([^:]+)[[:space:]]*:[[:space:]]*(.+)$ ]]; then
local group="${BASH_REMATCH[1]}"
local tag="${BASH_REMATCH[2]}"
local value="${BASH_REMATCH[3]}"
# Trim spaces from tag name
tag=$(echo "$tag" | sed 's/[[:space:]]*$//')
# Prefer DateTimeOriginal, then CreateDate, then DateTime
if [[ "$tag" == "DateTimeOriginal" && -z "$create_date" ]]; then
create_date="$value"
date_source="$group:$tag"
exif_found=1
elif [[ "$tag" == "CreateDate" && "$date_source" != *"DateTimeOriginal"* ]]; then
create_date="$value"
date_source="$group:$tag"
exif_found=1
elif [[ "$tag" == "DateTime" && -z "$create_date" ]]; then
create_date="$value"
date_source="$group:$tag"
exif_found=1
fi
fi
done <<< "$exif_output"
fi
# If no EXIF date found, try mediainfo for video files
if [[ -z "$create_date" ]] && command -v mediainfo &> /dev/null; then
local media_date=$(mediainfo --Inform="General;%Recorded_Date%" "$file" 2>/dev/null)
if [[ -n "$media_date" && "$media_date" != "0000-00-00 00:00:00" ]]; then
create_date="$media_date"
date_source="MediaInfo:Recorded_Date"
fi
fi
# If no EXIF or mediainfo date found, return failure
if [[ -z "$create_date" ]]; then
return 2 # No date metadata found
fi
# Convert EXIF format (YYYY:MM:DD HH:MM:SS) to standard format
# Always output as yyyy-mm-dd hh:mm:ss (pad single digits)
if [[ "$create_date" =~ ^([0-9]{4}):([0-9]{1,2}):([0-9]{1,2})[[:space:]]*([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})$ ]]; then
year="${BASH_REMATCH[1]}"
month=$(printf "%02d" "$((10#${BASH_REMATCH[2]}))")
day=$(printf "%02d" "$((10#${BASH_REMATCH[3]}))")
hour=$(printf "%02d" "$((10#${BASH_REMATCH[4]}))")
minute=$(printf "%02d" "$((10#${BASH_REMATCH[5]}))")
second=$(printf "%02d" "$((10#${BASH_REMATCH[6]}))")
create_date="$year-$month-$day $hour:$minute:$second"
else
# Try to convert yyyy-mm-dd hh:mm:ss (already correct)
if [[ "$create_date" =~ ^([0-9]{4})-([0-9]{2})-([0-9]{2})[[:space:]]*([0-9]{2}):([0-9]{2}):([0-9]{2})$ ]]; then
# Already correct
:
else
print_color "$RED" "Error: Cannot parse date '$create_date'" >&2
return 2
fi
fi
# For QuickTime files, the CreateDate is in UTC and needs conversion to local time
if [[ "$date_source" == *"QuickTime"* ]]; then
# Convert UTC time to local time
if [[ "$OSTYPE" == "darwin"* ]]; then
# On macOS, use TZ=UTC to interpret the input time as UTC
local utc_timestamp=$(TZ=UTC date -j -f "%Y-%m-%d %H:%M:%S" "$create_date" "+%s" 2>/dev/null)
if [[ -n "$utc_timestamp" ]]; then
create_date=$(date -j -r "$utc_timestamp" "+%Y-%m-%d %H:%M:%S" 2>/dev/null)
date_source="$date_source (converted from UTC)"
fi
else
local utc_timestamp=$(date -d "$create_date UTC" "+%s" 2>/dev/null)
if [[ -n "$utc_timestamp" ]]; then
create_date=$(date -d "@$utc_timestamp" "+%Y-%m-%d %H:%M:%S" 2>/dev/null)
date_source="$date_source (converted from UTC)"
fi
fi
fi
echo "$create_date|$date_source"
return 0
}
# Function to generate destination path based on organization pattern
generate_destination_path() {
local date_str="$1"
local original_filename="$2"
local base_destination="$3"
# Extract date components - handle both GNU and BSD date
local year month day hour minute second
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS (BSD date) - use more robust regex (allow single-digit month/day, tolerate extra spaces)
if [[ "$date_str" =~ ^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})[[:space:]]*([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})$ ]]; then
year="${BASH_REMATCH[1]}"
month=$(printf "%02d" "$((10#${BASH_REMATCH[2]}))")
day=$(printf "%02d" "$((10#${BASH_REMATCH[3]}))")
hour=$(printf "%02d" "$((10#${BASH_REMATCH[4]}))")
minute=$(printf "%02d" "$((10#${BASH_REMATCH[5]}))")
second=$(printf "%02d" "$((10#${BASH_REMATCH[6]}))")
else
return 1
fi
else
# Linux (GNU date)
year=$(date -d "$date_str" "+%Y" 2>/dev/null)
month=$(date -d "$date_str" "+%m" 2>/dev/null)
day=$(date -d "$date_str" "+%d" 2>/dev/null)
hour=$(date -d "$date_str" "+%H" 2>/dev/null)
minute=$(date -d "$date_str" "+%M" 2>/dev/null)
second=$(date -d "$date_str" "+%S" 2>/dev/null)
fi
if [[ -z "$year" || -z "$month" || -z "$day" ]]; then
return 1
fi
# Get file extension
local extension="${original_filename##*.}"
local lowercase_ext=$(echo "$extension" | tr '[:upper:]' '[:lower:]')
# Generate path and filename based on organization pattern
local dir_path=""
local filename=""
if [[ $FORCE_FULL_DATE -eq 1 ]]; then
dir_path="$base_destination"
filename="${year}-${month}-${day}_${hour}-${minute}-${second}.${lowercase_ext}"
else
case "$ORGANIZATION" in
"y")
dir_path="$base_destination/$year"
filename="${month}-${day}_${hour}-${minute}-${second}.${lowercase_ext}"
;;
"m")
dir_path="$base_destination/$year/$month"
filename="${day}_${hour}-${minute}-${second}.${lowercase_ext}"
;;
"d")
dir_path="$base_destination/$year/$month/$day"
filename="${month}-${day}_${hour}-${minute}-${second}.${lowercase_ext}"
;;
"h")
dir_path="$base_destination/$year/$month/$day/$hour"
filename="${minute}-${second}.${lowercase_ext}"
;;
*)
log_message "Invalid organization pattern: $ORGANIZATION" "ERROR"
return 1
;;
esac
fi
echo "$dir_path/$filename"
return 0
}
# Function to find files matching patterns
find_source_files() {
local files=()
if [[ ${#SOURCE_PATTERNS[@]} -eq 0 ]]; then
# Default: find all media files in current directory and subdirectories
# Exclude destination directory if it's a subdirectory of current directory
local find_cmd="find -L . -type f"
# Add exclusion for destination if it's relative to current directory
if [[ "$DESTINATION" =~ ^\./.*$ ]] || [[ "$DESTINATION" =~ ^[^/].*$ ]]; then
local abs_dest=$(cd "$DESTINATION" 2>/dev/null && pwd)
local abs_current=$(pwd)
if [[ "$abs_dest" == "$abs_current"* ]]; then
find_cmd="$find_cmd ! -path \"$DESTINATION/*\""
fi
fi
# Add media file extensions
local extensions=("*.jpg" "*.jpeg" "*.png" "*.tiff" "*.tif" "*.cr2" "*.nef" "*.arw" "*.dng" "*.raw" "*.mp4" "*.mov" "*.avi" "*.mts" "*.m2ts" "*.mkv" "*.wmv" "*.3gp" "*.m4v")
local ext_pattern=""
for ext in "${extensions[@]}"; do
if [[ -n "$ext_pattern" ]]; then
ext_pattern="$ext_pattern -o"
fi
ext_pattern="$ext_pattern -iname $ext"
done
eval "$find_cmd \\( $ext_pattern \\)" | while IFS= read -r file; do
echo "$file"
done
else
# Use specified patterns
for pattern in "${SOURCE_PATTERNS[@]}"; do
# Handle different pattern types
if [[ -d "$pattern" ]]; then
# Directory pattern
find -L "$pattern" -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.tiff" -o -iname "*.tif" -o -iname "*.cr2" -o -iname "*.nef" -o -iname "*.arw" -o -iname "*.dng" -o -iname "*.raw" -o -iname "*.mp4" -o -iname "*.mov" -o -iname "*.avi" -o -iname "*.mts" -o -iname "*.m2ts" -o -iname "*.mkv" -o -iname "*.wmv" -o -iname "*.3gp" -o -iname "*.m4v" \) 2>/dev/null
elif [[ "$pattern" == *"*"* ]] || [[ "$pattern" == *"?"* ]]; then
# Glob pattern
for file in $pattern; do
if [[ -f "$file" ]]; then
echo "$file"
fi
done
else
# Exact file or directory
if [[ -f "$pattern" ]]; then
echo "$pattern"
elif [[ -d "$pattern" ]]; then
find -L "$pattern" -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.tiff" -o -iname "*.tif" -o -iname "*.cr2" -o -iname "*.nef" -o -iname "*.arw" -o -iname "*.dng" -o -iname "*.raw" -o -iname "*.mp4" -o -iname "*.mov" -o -iname "*.avi" -o -iname "*.mts" -o -iname "*.m2ts" -o -iname "*.mkv" -o -iname "*.wmv" -o -iname "*.3gp" -o -iname "*.m4v" \) 2>/dev/null
fi
fi
done
fi
}
# Function to process a single file
process_file() {
local file="$1"
local file_size=$(get_file_size "$file")
TOTAL_SIZE=$((TOTAL_SIZE + file_size))
log_message "Processing: $file" "INFO"
# Extract date information
local date_info=$(extract_file_date "$file")
local extract_status=$?
if [[ $extract_status -eq 2 ]]; then
if [[ $COLLECT_UNSORTABLE -eq 1 ]]; then
local unsortable_dir="$DESTINATION/unsortable"
mkdir -p "$unsortable_dir"
local unsortable_path="$unsortable_dir/$(basename "$file")"
if [[ $DRY_RUN -eq 1 ]]; then
print_color "$BLUE" "Would move unsortable: $file -> $unsortable_path"
else
if mv "$file" "$unsortable_path"; then
log_message "Unsortable: $file -> $unsortable_path" "SUCCESS"
else
log_message "Failed to move unsortable file: $file" "ERROR"
fi
fi
SKIPPED_FILES=$((SKIPPED_FILES + 1))
else
log_message "Could not extract date from $file - skipping" "WARNING"
SKIPPED_FILES=$((SKIPPED_FILES + 1))
fi
return 1
elif [[ $extract_status -ne 0 ]] || [[ -z "$date_info" ]]; then
log_message "Could not extract date from $file - skipping" "WARNING"
SKIPPED_FILES=$((SKIPPED_FILES + 1))
return 1
fi
local date_str="${date_info%|*}"
local date_source="${date_info#*|}"
log_message "Date: $date_str (from $date_source)" "INFO"
# Generate destination path
local dest_path=$(generate_destination_path "$date_str" "$(basename "$file")" "$DESTINATION")
if [[ $? -ne 0 ]] || [[ -z "$dest_path" ]]; then
log_message "Could not generate destination path for $file" "ERROR"
ERROR_FILES=$((ERROR_FILES + 1))
FATAL_ERROR=1
return 2
fi
# Handle filename conflicts
local counter=1
local original_dest_path="$dest_path"
while [[ -f "$dest_path" ]]; do
local dir_path=$(dirname "$original_dest_path")
local filename=$(basename "$original_dest_path")
local name_without_ext="${filename%.*}"
local ext="${filename##*.}"
dest_path="$dir_path/${name_without_ext}_${counter}.${ext}"
counter=$((counter + 1))
done
local dest_dir=$(dirname "$dest_path")
if [[ $DRY_RUN -eq 1 ]]; then
if [[ $KEEP_ORIGINALS -eq 1 ]]; then
print_color "$BLUE" "Would copy: $file -> $dest_path"
else
print_color "$BLUE" "Would move: $file -> $dest_path"
fi
PROCESSED_FILES=$((PROCESSED_FILES + 1))
PROCESSED_SIZE=$((PROCESSED_SIZE + file_size))
return 0
fi
# Create destination directory
if ! mkdir -p "$dest_dir"; then
log_message "Could not create directory: $dest_dir" "ERROR"
ERROR_FILES=$((ERROR_FILES + 1))
return 1
fi
# Copy or move file
if [[ $KEEP_ORIGINALS -eq 1 ]]; then
if cp "$file" "$dest_path"; then
log_message "Copied: $file -> $dest_path" "SUCCESS"
PROCESSED_FILES=$((PROCESSED_FILES + 1))
PROCESSED_SIZE=$((PROCESSED_SIZE + file_size))
return 0
else
log_message "Failed to copy: $file" "ERROR"
ERROR_FILES=$((ERROR_FILES + 1))
return 1
fi
else
if mv "$file" "$dest_path"; then
log_message "Moved: $file -> $dest_path" "SUCCESS"
PROCESSED_FILES=$((PROCESSED_FILES + 1))
PROCESSED_SIZE=$((PROCESSED_SIZE + file_size))
return 0
else
log_message "Failed to move: $file" "ERROR"
ERROR_FILES=$((ERROR_FILES + 1))
return 1
fi
fi
}
# Function to display final report
show_report() {
local end_time=$(date +%s)
local elapsed_time=$((end_time - START_TIME))
local hours=$((elapsed_time / 3600))
local minutes=$(((elapsed_time % 3600) / 60))
local seconds=$((elapsed_time % 60))
echo ""
print_color "$GREEN" "=========================================="
print_color "$GREEN" " PROCESSING REPORT"
print_color "$GREEN" "=========================================="
echo ""
echo "Files Summary:"
echo " Total files found: $TOTAL_FILES"
echo " Successfully processed: $PROCESSED_FILES"
echo " Skipped (no date): $SKIPPED_FILES"
echo " Errors: $ERROR_FILES"
echo ""
echo "Size Summary:"
echo " Total size found: $(format_size $TOTAL_SIZE)"
echo " Successfully processed: $(format_size $PROCESSED_SIZE)"
echo ""
echo "Time Summary:"
printf " Time elapsed: %02d:%02d:%02d\n" $hours $minutes $seconds
if [[ $elapsed_time -gt 0 && $PROCESSED_FILES -gt 0 ]]; then
local files_per_second=$((PROCESSED_FILES / elapsed_time))
local mb_per_second=$((PROCESSED_SIZE / elapsed_time / 1048576))
echo " Speed: $files_per_second files/sec, ${mb_per_second}MB/sec"
fi
echo ""
if [[ $DRY_RUN -eq 1 ]]; then
print_color "$YELLOW" "DRY RUN MODE - No files were actually moved/copied"
elif [[ $KEEP_ORIGINALS -eq 1 ]]; then
print_color "$BLUE" "COPY MODE - Original files were preserved"
else
print_color "$GREEN" "MOVE MODE - Files were moved to destination"
fi
echo ""
print_color "$GREEN" "=========================================="
}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-o|--organization)
ORGANIZATION="$2"
if [[ ! "$ORGANIZATION" =~ ^[ymdh]$ ]]; then
print_color "$RED" "Error: Invalid organization pattern. Must be one of: y, m, d, h"
exit 1
fi
shift 2
;;
--full-date)
FORCE_FULL_DATE=1
shift
;;
--collect-unsortable)
COLLECT_UNSORTABLE=1
shift
;;
-s|--source)
SOURCE_PATTERNS+=("$2")
shift 2
;;
-d|--destination)
DESTINATION="$2"
shift 2
;;
-k|--keep-originals)
KEEP_ORIGINALS=1
shift
;;
--dry-run)
DRY_RUN=1
shift
;;
-v|--verbose)
VERBOSE=1
shift
;;
-h|--help)
show_help
exit 0
;;
--version)
show_version
exit 0
;;
*)
print_color "$RED" "Error: Unknown option: $1"
echo "Use -h or --help for usage information."
exit 1
;;
esac
done
# Set default destination if not specified
if [[ -z "$DESTINATION" ]]; then
DESTINATION="./sorted"
fi
# If no organization is provided, default to flat full-date naming
if [[ -z "$ORGANIZATION" ]]; then
FORCE_FULL_DATE=1
fi
# Convert destination to absolute path
DESTINATION=$(cd "$(dirname "$DESTINATION")" 2>/dev/null && pwd)/$(basename "$DESTINATION") || DESTINATION=$(realpath "$DESTINATION" 2>/dev/null) || DESTINATION="$DESTINATION"
# Display configuration
print_color "$GREEN" "$SCRIPT_NAME v$VERSION"
echo ""
echo "Configuration:"
echo " Organization pattern: $ORGANIZATION"
echo " Destination: $DESTINATION"
echo " Keep originals: $([ $KEEP_ORIGINALS -eq 1 ] && echo "Yes" || echo "No")"
echo " Dry run: $([ $DRY_RUN -eq 1 ] && echo "Yes" || echo "No")"
echo " Verbose: $([ $VERBOSE -eq 1 ] && echo "Yes" || echo "No")"
if [[ ${#SOURCE_PATTERNS[@]} -gt 0 ]]; then
echo " Source patterns:"
for pattern in "${SOURCE_PATTERNS[@]}"; do
echo " - $pattern"
done
else
echo " Source patterns: All media files in current directory"
fi
echo ""
# Check dependencies
check_dependencies
# Create destination directory if it doesn't exist (unless dry run)
if [[ $DRY_RUN -eq 0 ]]; then
if ! mkdir -p "$DESTINATION"; then
print_color "$RED" "Error: Cannot create destination directory: $DESTINATION"
exit 1
fi
fi
# Find all source files
print_color "$BLUE" "Scanning for media files..."
files=()
while IFS= read -r file; do
files+=("$file")
done < <(find_source_files)
TOTAL_FILES=${#files[@]}
if [[ $TOTAL_FILES -eq 0 ]]; then
print_color "$YELLOW" "No media files found matching the specified patterns."
exit 0
fi
print_color "$BLUE" "Found $TOTAL_FILES media files to process"
echo ""
# Process each file
FATAL_ERROR=0
for file in "${files[@]}"; do
if [[ -f "$file" ]]; then
process_file "$file"
if [[ $FATAL_ERROR -eq 1 ]]; then
print_color "$RED" "Fatal error encountered. Stopping further processing."
break
fi
fi
done
# Show final report
show_report
# Exit with appropriate code
if [[ $ERROR_FILES -gt 0 ]]; then
exit 1
else
exit 0
fi