3

I have a script that performs a SQL SELECT and sends some e-mails using the output but there is an issue with the script causing the error message:

line 34: [email protected]: syntax error: invalid arithmetic operator (error token is "@mail.com")

Line 34 is:

if (( ${msgData[$email]:-0} < $(LC_TIME=C date -d yesterday +%s) )); then

Here is the script:

#!/bin/bash
${BASH_VERSION+shopt -s extglob lastpipe} 2>/dev/null

# Full path to a file to store our date offset data. Will be overwritten.
datefile=date.log

# Assign your vars or insert whatever code does so here.
user=user pass=pass db=db

function doit {
    typeset x
    for x; do
        if [[ -z $x ]]; then
            echo 'doit: missing an argument.' >&2
            return 1
        fi
    done

    typeset template=$(</dev/fd/4)
    exec 4<&-
    sqlplus "${1}/${2}@${3}" <&3 | {
        if [[ -f $datefile ]]; then
            . "$datefile" || return 1
        else
            # An associative array that maps from num -> timestamp
            typeset -A msgData
        fi

        # If we've successfully read the file containing timestamps, then overwrite with new data on RETURN
        ${msgData+trap 'trap RETURN; typeset -p msgData >"$datefile"' RETURN}

        while IFS=, read -r num email limit orders; do
            ${email:+:} continue
            if (( ${msgData[$email]:-0} < $(LC_TIME=C date -d yesterday +%s) )); then
                printf -- "$template" "email" "$num" "$limit" "$orders" |
                    /usr/sbin/sendmail -f [email protected] -oi -t

                msgData[$email]=$(LC_TIME=C date +%s)
            else
                printf 'Mail already sent to %s within the last 24 hours... skipping.\n' "$email" >&2
            fi
        done
    }
} 5<&0 <<\SQL 3<&0 <<\TEMPLATE 4<&0 <&5-
set pagesize 0
set feedback 0

SELECT kred_lim.kunr ||','|| kust_adr.ku_email ||','|| kred_lim.kred_limit ||','|| kred_lim.kred_zu_zahlen
FROM kred_lim, kust_adr
WHERE kred_lim.kred_zu_zahlen > kred_lim.kred_limit
AND kred_lim.kunr = kust_adr.ku_nr;
SQL
Subject: Credit limit
To: %s

Customer number: %s
Credit limit: %s
Current orders: %s
TEMPLATE

if ! doit "$user" "$pass" "$db"; then
    echo 'we failed :(' >&2
    exit 1
fi
3
  • What line is the 34th? Can you highlight it somehow? Commented Jul 8, 2013 at 12:20
  • probably ${msgData[$email]:-0} contains a nonumeric string Commented Jul 8, 2013 at 12:43
  • make a test harness in a separate file that just sets the miniumum number of variables and the line in question, and add set -vx to see what is happening (as best you can) inside the command-substituions, etc. Good luck. Commented Jul 9, 2013 at 4:39

2 Answers 2

3

The problem is that you are trying to perform a string comparison inside an arithmetic expression. Either switch to a compound expression:

if [[ ${msgData[$email]:-0} < $(LC_TIME=C date -d yesterday +%s) ]]; then

or check that you are accessing the correct element of msgData.

Sign up to request clarification or add additional context in comments.

3 Comments

Changing that line to that has made no difference and the error message is still the same.
Hm. Is it possible that you are reaching that line without msgData having been declared as an associative array? I.e, datefile exists and is sourced, but exits with status 0 so that you don't return.
I'm not sure, at the moment datefile doesn't exist at all as I can't run the script at the moment due to the error message so I guess not?
2

It first seemed to me that this is a limitation of the implementation of associative arrays in bash (as of v4.3.11 here).

The documentation says:

Associative arrays are created using

declare -A name.

Arrays are assigned to using compound assignments of the form

name=(value1 value2 … )

where each value is of the form [subscript]=string.

And then you look up subscript a few lines earlier:

The subscript is treated as an arithmetic expression that must evaluate to a number.

So it's saying a string "foo@bar" can't evaluate to a number, and can't be used as an array key? But that doesn't explain why strings without "@" can.

The confusing part is also that when you do the assignment manually, e.g.

declare -A emails_to_accounts=( \
    ["[email protected]"]="baz" \
    ["[email protected]"]="quux" \
)

for email in "${!emails_to_accounts[@]}"; do
    name=${emails_to_accounts[${email}]}
    : [...whatever...]
done

That works fine. Bother.

The solution for this, as it turns out, is to repeat the declare -A part in the assignment, like this:

declare -A "hash_name+=([$email]=$whatever)"

In the specific example above, it's probable that ${msgData[$email]:-0} gets evaluated in integer context because you use the < comparison.

So I'd wager that you want to do something like quote it to escape that context first.

Or simply do it with fewer shorthands, without the :- modifier, which really contributes to the impression of line noise when combined with all the other punctuation on that line. For example:

msgTime=${msgData[$email]}
test -n "$msgTime" || msgTime=0
if [ $msgTime -lt $(LC_TIME=C date -d yesterday +%s) ]; then

Comments

Your Answer

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