1

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.

5 Answers 5

3

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.

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

3 Comments

I'm afraid, that might be true. My code won't be readable any more. But be honest... What satisfies you more? A case command with a long hard coded list or a real calculation which results in exactly what you want? ;)
@lumbric, yes, I sometimes still get a sense of achievement from "clever" code even after so many decades but, as a professional, I'm paid to deliver quality rather than cleverness. Not that they're always necessarily mutually exclusive.
Even cryptic one-liners can be made clearer with a single comment.
2
$ date "+( ( %m - 1 ) / 3 + 1 ) * 3 % 12 + 1" | xargs expr
1

Comments

1

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

Comments

1

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

2 Comments

Ah that's a nice way, I didn't think about that. With bash there is no need to escape %. At least this was the result of my tests.
Date, not the shell, is involved in processing % or %%; % needs to be escaped when using non-GNU versions of date - here on Mac OS X I have both versions of date installed and I could verify the difference. Anyway, I recommend %% because it is more portable.
0

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

2 Comments

The format you use in the date command yields spaces. But expr only allows certain forms of arguments with spaces. It won't allow ( expr ) * something, which is why you get the error on the star.
I don't think this a problem. Actually I need the spaces for expr. Otherwise expr just returns the input as string (as in your code in the first line).

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.