0

I'm writing a simple Bash script to execute a backup and send a mail at the end with a modified subject if the command has been failed.

This is a snippet of the script:

BACKUP_CMD="ls -1" # Just an example command
MAIL_RECIPIENTS="me@domain" # Recipient addresses (space separated list)
MAIL_SUBJECT="Backup script" # Mail subject
MAIL_FAIL_SUBJECT_PREPEND="[!!! ---FAILURE--- !!!] "
MAIL_CMD="mail -a \"Content-Type: text/plain; charset=UTF-8\" -s \"SUBJECT\" RECIPIENTS" # Mail send command

# Execute backup
output="$($BACKUP_CMD)"
status=$?

# Check exit status code and compose the mail message
echo $status
if [ "$status" -eq "0" ]; then
    mail_subject=$MAIL_SUBJECT
    mail_text="${output}"
else
    mail_subject="$MAIL_FAIL_SUBJECT_PREPEND $MAIL_SUBJECT"
    mail_text="$output\n\nExit status code: $status"
fi

# Compose the mail command with subject and recipients list
mail_cmd=$MAIL_CMD
mail_cmd="${mail_cmd/SUBJECT/$mail_subject}"
mail_cmd="${mail_cmd/RECIPIENTS/$MAIL_RECIPIENTS}"

echo "$mail_text" | ${mail_cmd}

The mail is not sent and the script exits with this error from send-mail:

send-mail: RCPT TO:<Backup", charset=UTF-8"@server.domain> (501 5.1.3 Bad recipient address syntax)
Can't send mail: sendmail process failed with error code 1

It seems that bash "scrambles" arguments at the end of the mail command.

What I cannot understand is that if I print $mail_cmd it seems to be correct:

mail -a "Content-Type: text/plain; charset=UTF-8" -s "Backup script" me@domain

Where I'm wrong?

1
  • Och my, don't use BACKUP_CMD="ls -1" and MAIL_CMD="mail -a .... Use bash arrays. MAIL_CMD=(mail -a "blabla" "blablabla"). Commented Aug 2, 2019 at 10:43

1 Answer 1

1

Your script could be fixed with some bash arrays I believe:

backup_cmd=(ls -1)
mail_cmd2=(mail -a "Content-Type: text/plain; charset=UTF-8" -s)

...

output=$("${backup_cmd[@]}")

...

mail_cmd=("${mail_cmd2[@]}")
mail_cmd+=("$mail_subject")
mail_cmd+=( $MAIL_RECIPIENTS )
# MAIL_RECIPIENTS  should be changed to a bash array too!
echo "$mail_text" | "${mail_cmd[@]}"

But would be way better to refactor the script and create a function:

mail_cmd() {
    local subject recipients
    subject="$1"
    shift
    recipients=("$@")
    mail -a "Content-Type: text/plain; charset=UTF-8" -s "$subjsect" "${recipients[@]}"
}

mail_recipients=( "me@domain" "other@otherdomain" )

...

printf "%s\n" "$mail_text" | mail_cmd "$subject" "${mail_recipients[@]}"

Notes:

  • It is common to use UPPER_CASE_VARIABLES for exported variables from your script, not local.
  • You can export functions too.

I print $mail_cmd it is correct

But the space separation happens, but the " quote interpretation does not. What I mean is that the line:

mail -a "Content-Type: text/plain; charset=UTF-8" -s "Backup script" me@domain

is interpreted as (added single quotes):

mail -a '"Content-Type:' 'text/plain;' 'charset=UTF-8"' -s '"Backup' 'script"' me@domain

and mail fails because ex. text/plain is not a valid mail address.

mail_text="$output\n\nExit status code: $status"

The \n are not newlines here, but literally \ slash and n n character. You can embed a newline with simple $'\n' construct like:

 mail_text="$output"$'\n\n'"Exit status code: $status"
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.