lumbric
lumbric

Reputation: 9093

Syntax error with expr in bash

I'm trying to get the first day of the next quartal, for example now I'd like to get 00:01 2012-01-01. It shouldn't be too difficult. But I'm stuck with a syntax error because I can't manage to escape everything correctly.

To get the correct month I want to use:

expr `date "+( ( %m - 1 ) / 3 + 1 ) * 3 % 12 + 1"`

The part ( %m - 1 ) / 3 + 1 calculates the number of the current quartal. However, the * seems to be wrong there somehow. If I replace it by + there is no syntax error (the result is just not interesting for me...). I tried to escape the * by using \* but this doesn't help. I suppose there is some problem with the backticks.

How can I escape the * correctly?

Edit: My problem is already solved, but I want to add a short warning for others. Note that you would need to add also leading zeros for the month, if you want to use it directly in a date and to care also about the correct year. So at the end of the day, paxdiablo's suggestion is probably the easiest.

Upvotes: 1

Views: 4087

Answers (5)

paxdiablo
paxdiablo

Reputation: 882226

If you don't mind coding up a bash function to do this, you can use something like the following getNextQuarter function:

# getNextQuarter - returns YYYY-MM-DD for the first day of the next quarter.
getNextQuarter() {
    m="$(date +%m)"
    case $m in
        '01' | '02' | '03') echo "$(date +%Y)-04-01" ;;
        '04' | '05' | '06') echo "$(date +%Y)-07-01" ;;
        '07' | '08' | '09') echo "$(date +%Y)-10-01" ;;
        '10' | '11' | '12') echo "$(expr $(date +%Y) + 1)-01-01" ;;
    esac
}

nq=$(getNextQuarter)
echo $nq

Even in a shell script, you should consider the readability improvements that can be gained from using well-named functions over possibly cryptic one-liners.

Now that probably won't be blindingly fast but, to be honest, if speed was your main concern, I would be revisiting shell scripts anyway.

Think of what others (or even you six months from now) will think of your code when viewing it. I like to live on the assumption that those who have to maintain my code are psychopaths who know where I live.

Upvotes: 3

Blaisorblade
Blaisorblade

Reputation: 6488

The first problem was that * was considered a wildcard after being output by date. I solved this using $[] (equivalent to $(())) instead of expr.

The second issue (at least here on Mac OS X) is that the '%' needs to be escaped by using '%%' (which is valid on all versions of date):

$ echo $(($(date "+( ( %m - 1 ) / 3 + 1 ) * 3 %% 12 + 1")))
1

An alternative I found was using eval, after correctly escaping parentheses:

$ eval "expr $(date "+\( \( %m - 1 \) / 3 + 1 \) '*' 3 %% 12 + 1")"

Both alternatives can be zero-padded using printf:

$ printf "%02d\n" $(($(date "+( ( %m - 1 ) / 3 + 1 ) * 3 %% 12 + 1")))
01
$ printf "%02d\n" $(eval "expr $(date "+\( \( %m - 1 \) / 3 + 1 \) '*' 3 %% 12 + 1")")
01

Upvotes: 1

kev
kev

Reputation: 161914

$ date "+( ( %m - 1 ) / 3 + 1 ) * 3 % 12 + 1" | xargs expr
1

Upvotes: 2

Adam Zalcman
Adam Zalcman

Reputation: 27233

You can disable glob expansion using set, run your calculation and re-enable glob expansion:

set -o noglob
expr `date "+( ( %m - 1 ) / 3 + 1 ) * 3 % 12 + 1"`
set +o noglob

Upvotes: 1

fge
fge

Reputation: 121810

Surround your whole expression with double quotes: the problem is that you have spaces in your command substitution, which doesn't please expr.

This yields a number, however I surmise you can use arithmetic evaluation more efficiently there:

fge@erwin ~ $ expr "`date "+( ( %m - 1 ) / 3 + 1 ) * 3 % 12 + 1"`"
( ( 12 - 1 ) / 3 + 1 ) * 3 % 12 + 1
fge@erwin ~ $ echo $(($(expr "`date "+( ( %m - 1 ) / 3 + 1 ) * 3 % 12 + 1"`")))
1

Upvotes: 0

Related Questions