This is my script which I am running on UNIX(AIX):
$MON date +"%m"
echo 'expr $MON - 2'
Output:
04
expr $MON - 2
I just want to subtract 2 from my current month and display .
I realize that the default shell on AIX is some variant of ksh which doesn't suffer from the same deficiency as bash in the input base, but it is something to bear in mind if you end up encountering this on another platform.
If this is your input:
$MON date +"%m"
echo 'expr $MON - 2'
then you have several issues.
$MON is unset, which causes the invocation of the line date +"%m", which gives the output 04
the echo command does not execute the expr command as you're using the wrong kind of ticks, which causes the output expr $MON - 2
First, to assign the variable you do VARIABLE=value, in your case this should be:
MON=`date +"%m"`
Don't put a space before or after the = sign.
Secondly, to perform the expr, you need to use the backtick(`)
echo `expr $MON - 2`
However, for most shells, you should use the more modern version of i want to get the result of a command, which is the logic $(command). These can be embedded, which makes them a lot easier to understand (backticks require escaping, and the more backticks the more escaping needed which quickly leads to backslash-palooza).
For bash, you need to ensure that the month is interpreted as a base 10 number, as otherwise once you hit August the code will stop working:
To force the number to be interpreted as a base 10 number, precede it with 10#:
MON=10#$(date +"%m")
echo $(($MON-2))
Examples:
bash-3.2$ month=07
bash-3.2$ echo $(($month + 1))
8
bash-3.2$ month=08
bash-3.2$ echo $(($month + 1))
bash: 08: value too great for base (error token is "08")
bash-3.2$ month=10#08
bash-3.2$ echo $(($month + 1))
9
Try this:
MON=$(date +"%m")
echo $(($MON-2))
You don't need to use expr because bash can perform simple arithmetic as well.
If your shell doesn't support arithmetic expressions, use expr:
expr $MON - 2
In both cases, you will get 2 as the output.
Another option: if you want to see 11 or 12 in January or February, let GNU date do the arithmetic:
date -d "-2 months" +%m
Related
I'm trying to get the week number of last week. The following command normally had work, but now I'm getting error.
lastweeknumber=$((`date +%V`-1))
bash: 09: value too great for base (error token is "09")
This week number is 09, so I've tried to convert to decimal adding 10# like this $(10#(date +%V)) but it's not working.
How to fix this?
Consider the following, which uses bash's built-in functionality in place of the external date command, and thus requires a recent shell release but is much faster to run (and will behave consistently without depending on a specific version of date).
With that done, though, there's still a need to strip the leading 0 -- which a parameter expansion will do just fine:
printf -v seconds_now '%(%s)T' -1
printf -v weeknum_lastweek '%(%V)T' "$(( seconds_now - (60 * 60 * 24 * 7) ))"
echo "The index of last week is ${weeknum_lastweek#0}"
It is because date +%V returns 09 and shell is interpreting any value starting with 0 as an octal number. Note that 09 is an invalid octal number hence you get that error value too great for base.
You can just force module 10 arithmetic in (( ... )):
echo $(( 10#$(date +%V) - 1 ))
8
Another way that handles wrapping around year correctly:
lastweeknumber=$(date -d "1 week ago" +%V)
Here is my script:
d1=0.003
d2=0.0008
d1d2=$((d1 + d2))
mean1=7
mean2=5
meandiff=$((mean1 - mean2))
echo $meandiff
echo $d1d2
But instead of getting my intended output of:
0.0038
2
I am getting the error Invalid Arithmetic Operator, (error token is ".003")?
bash does not support floating-point arithmetic. You need to use an external utility like bc.
# Like everything else in shell, these are strings, not
# floating-point values
d1=0.003
d2=0.0008
# bc parses its input to perform math
d1d2=$(echo "$d1 + $d2" | bc)
# These, too, are strings (not integers)
mean1=7
mean2=5
# $((...)) is a built-in construct that can parse
# its contents as integers; valid identifiers
# are recursively resolved as variables.
meandiff=$((mean1 - mean2))
Another way to calculate floating numbers, is by using AWK rounding capability, for example:
a=502.709672592
b=501.627497268
echo "$a $b" | awk '{print $1 - $2}'
1.08218
In case you do not need floating point precision, you may simply strip off the decimal part.
echo $var | cut -d "." -f 1 | cut -d "," -f 1
cuts the integer part of the value. The reason to use cut twice is to parse integer part in case a regional setting may use dots to separate decimals and some others may use commas.
Edit:
Or, to automate the regional settings one may use locale.
echo $var | cut -d $(locale decimal_point) -f 1
You can change the shell which you are using. If you are executing your script with bash shell bash scriptname.sh try using ksh for your script execution. Bash doesn't support arithmetic operations that involve floating point numbers.
Big shout-out to the bc command - it totally saved my day! It's a simple answer, but it worked like a charm.
a=1.1
b=1.1
echo $a + $b | bc -l
# Output:
2.2
#SUM
sum=$(echo $a + $b | bc -l)
echo $sum
# Output
2.2
bc is a command-line calculator, which allows users to perform mathematical calculations on the terminal.
Year=`date '+%Y'`
RTRN1=$?
This returns the current date in the logs, however i want to return the year before, so instead of this returning 2017 i want 2016.
Any help appreciated! Thanks
For GNU date utility: use -d (--date) option to adjust the date:
Year=$(date +%Y -d'1 year ago')
echo $Year
2016
As we have bash, it's possible to use let
let YEAR=`date +%Y`-1
echo $YEAR
You can always capture the year with date +'%Y'. You can subtract 1 with the POSIX arithmetic operator, e.g.
$ echo $(($(date +%Y) - 1))
2016
You can also use the POSIX compliant expr math operators, e.g.
$ expr $(date +%Y) - 1
2016
(note: with expr you must leave a space between the math operator and the values)
The GNU date operator -d with '1 year ago' will work as specified in the comments and other answer, along with let dt=$(date +%Y)-1; echo $dt as specified in the other answer (no spaces allowed with let).
Of all the choices, if I didn't have GNU date, I'd pick the POSIX arithmetic operator $((...)) with a date command substitution minus 1.
I am trying to get the time difference between two dates as given below in Bash script. However I am not successful
head_info: 05-31-2017:04:27:37
tail_info: 05-31-2017:04:30:57
the problem is that after Reformation above time and while trying to calculate in seconds due to space, it is ignoring time.
This is my script:
fm_head_info=(${head_info:6:4}"-"${head_info:0:2}"-"${head_info:3:2}" \
"${head_info:11:8})
fm_tail_info=(${tail_info:6:4}"-"${tail_info:0:2}"-"${tail_info:3:2}" \
"${tail_info:11:8})
$ fm_head_info
-bash: 2017-05-31: command not found
Thank you
Let's define your shell variables:
$ tail_info=05-31-2017:04:30:57
$ head_info=05-31-2017:04:27:37
Now, let's create a function to convert those dates to seconds-since-epoch:
$ date2sec() { date -d "$(sed 's|-|/|g; s|:| |' <<<"$*")" +%s; }
To find the time difference between those two date in seconds:
$ echo $(( $(date2sec "$tail_info") - $(date2sec "$head_info") ))
200
As written above, this requires bash (or similar advanced shell) and GNU date. In other words, this should work on any standard Linux. To make this work on OSX, some changes to the date command will likely be necessary.
How it works
Starting with the innermost command inside the function date2sec, we have:
sed 's|-|/|g; s|:| |' <<<"$*"
In the argumnet to the function, this replaces all - with / and it replaces the first : with a space. This converts the the dates from the format in your input to one that the GNU date function will understand. For example:
$ sed 's|-|/|g; s|:| |' <<<"05-31-2017:04:30:57"
05/31/2017 04:30:57
With this form, we can use date to find seconds-since-epoch:
$ date -d "05/31/2017 04:30:57" +%s
1496230257
And, for the head_info:
$ date -d "05/31/2017 04:27:37" +%s
1496230057
Now that we have that, all that is left is to subtract the times:
$ echo $(( 1496230257 - 1496230057 ))
200
Your immediate issue is the inclusion of erroneous (...) surrounding your string indexed assignment and your questionable quoting. It looks like you intended:
fm_head_info="${head_info:6:4}-${head_info:0:2}-${head_info:3:2} ${head_info:11:8}"
fm_tail_info="${tail_info:6:4}-${tail_info:0:2}-${tail_info:3:2} ${tail_info:11:8}"
Your use of string indexes is correct, e.g.
#!/bin/bash
head_info=05-31-2017:04:27:37
tail_info=05-31-2017:04:30:57
fm_head_info="${head_info:6:4}-${head_info:0:2}-${head_info:3:2} ${head_info:11:8}"
fm_tail_info="${tail_info:6:4}-${tail_info:0:2}-${tail_info:3:2} ${tail_info:11:8}"
echo "fm_head_info: $fm_head_info"
echo "fm_tail_info: $fm_tail_info"
Example Use/Output
$ bash headinfo.sh
fm_head_info: 2017-05-31 04:27:37
fm_tail_info: 2017-05-31 04:30:57
You can then do something similar with the differences in date -d "$var" +%s as John shows in his answer to arrive at the time difference. Note, string indexes are limited to bash, while a sed solution (absent the herestring) would be portable on all POSIX shells.
I'm running a pretty simple bash script in ubuntu but have come up with a problem.
If needed I'll post the whole script, but I've narrowed down the problem.
Basically, I want to run some code every 15 seconds, so I started with this:
time=`date +%S`
time2=$((time%15))
if [ $time2 -eq 0 ]
then
etc, etc, etc....
The problem comes up when time is 08 seconds. The script terminates with Illegal number: 08.
Adding to that, when using:
time2=$(($time%15))
instead of the illegal number error, it would terminate with Arithmetic expression: expecting EOF: "08%15"
I'm guessing 08 isn't interpreted as a number. Or there's some base issue, like it thinks it's in octal or something. Any help?
Shortest solution:
time2=$(( ${time#0} % 15 ))
${var#glob} means "$var with glob removed from the beginning if present".
Try using the following flags instead
date +%-S
It says given the -, it won't pad. It has problems with the base, interpreting it as an octal integer.
Anyway, if you want to do something every 15 seconds, i find this one easier to follow:
while ((1)); do
echo do something now...
sleep 15s
done
Force Bash to interpret the number in decimal, no matter how many padded zeros:
time2=$((10#$time % 15))
you're right, it was interpreting it as octal. bourne shells do that for any number with a leading 0 in an Arithmetic Substition:
#~ $ echo $(( 010 ))
8
#~ $ echo $(( 0100 ))
64
#~ $ echo $(( 10#0100 ))
100
#~ $ echo $(( 40#lolwut ))
2213236429
look in the manpage for 'base#' to see all the details about this '#-forcing' thing. you can get pretty ridiculous with it if you want to
Since you're only interested in "every fifteen seconds" rather than running things on the minute exactly you could use date +%s (lowercase s) which will give you the number of seconds since the start of the epoch. This won't have a leading 0 so your script should run fine.
However, I would wonder about your code in a wider context. If the system is running very slow for some reason it could be possible for the script only be run second 14 and then second 16 meaning it will miss an execution.
It might be worth touching a file when you do whatever it is the script does and then performing your action when then last modified date of the file is 15 or more seconds ago.
That does look like it's interpreting it as octal.
Try date +%S | sed -e 's/^0//'