Bash – Script to email all matching files in a directory

bashemailmuttshell-script

Trying to set up a bash script to automate sending data files. Desired end result is to check a specified directory once per week, make a list of all Excel files modified within the last 2 days, and email them to a designated recipient list.

I have sendmail configured and working (using a Gmail relay). I have mutt configured and working. In general the commands I am trying to send work if send directly from CLI (mail is sent and received, with attachments) but I get repeated failures when trying to call them from the script. Everything seems to stem from the fact that the directory and file names contain spaces. I can't change this – I don't have control over the naming of the files – but it seems as if this is the sticking point in my script.

Two main problems:

  1. If I try to send all files in one go, mutt reports the attachment(s) cannot be attached:
Can't stat "/path\ to/file1.xls
/path\ to/file2.xls": No such file or directory

The newline char is being passed as part of the $FILES variable.

  1. If I try to loop through the directory to send files one at a time (my desired outcome, as some files are fairly large) the script interprets spaces as delimiters, escaped or not – so /path/to/the\ files/file\ 1.xls is seen as 3 values (/path/to/the\, files/file\, 1.xls).

I did have the first half of the script working (mailing all files at once) but managed to break it trying to add the loop. Of course I didn't save the earlier working version. Tried using set ifs=$'\n' for the loop to get the delimiters correct, but when I have that in place mutt tells me the full path to the file is not a file, or that there was no recipient designated. It's a bit maddening.

Script:

#!/bin/bash
# This file should send an email to specified recipients with data files for the week attached.

# Set reply-to address for Mutt
export REPLYTO="my_email@domain.com"

# Replace with space separated email address of all recipients
EMAILS="recipient1@domain.com recipient2@domain.com"

# Get today's date for subject
# Date is in YYYY-MM-DD format
TODAY=$(date +%F)

# Set the message body
MBODY="Sending this week's data files.\n"

# Set the starting directory
# Don't bother escaping it, this is fixed in FILES variable below
DIR="/home/user/path to files"

# Get the list of files to send
FILES=$(find "$DIR" -type f -mtime -2 | sed 's/ /\\ /g' | grep ".xls")

# Check to see if we found any files or not
if [ -z "$FILES" ]; then
    MBODY="No matching files added or modified within last 2 days. No files sent.\n"
    echo "$MBODY" | mutt -s "Data files for $TODAY" $EMAILS
fi

# Send all files in a single email
echo $MBODY | mutt -s "Data files for $TODAY" -a "$FILES" -- $EMAILS

For sending files separately, I tried the following instead of the last 2 lines above:

# Cycle through FILES array and send each file individually
for datafile in ${FILES[*]}
do
   set IFS=$'\n\t'
   echo $MBODY | mutt -s \"Data files for $TODAY\" -a $datafile -- $EMAILS 
   unset $IFS
done

Any help? I've been stuck on this for a while now. I'm not married to using mutt, or even bash for that matter, if this is easier done in another language.

Best Answer

set "/home/user/path to files/"*.xls
for f do [ "$f" -nt "$two_day_old_file" ] && set "$@" "$f" ; shift ; done
touch "$two_day_old_file"
echo $MBODY | mutt -s "Data files for $TODAY" -a "$@" -- $EMAILS    

To mail them one at a time change the echo line to:

for mailf do echo "$MBODY" | 
    mutt -s "Data files for $TODAY" -a "$mailf" -- $EMAILS
done

Would probably work, but your real problem lies here:

...
set IFS=...
...

This doesn't affect the value of the internal field separator at all, but rather it assigns the value IFS=... to the first positional parameter, or $1. $IFS remains valued at whatever it was before you set $1. You just need to do:

IFS='
   ' 

Or...

IFS=${IFS# } 

...if $IFS is set to the default value, which it must be if this is an executable script and you have not altered $IFS anywhere else in the script.

Related Question