I wrote below piece of code to calculate the date after a given date:
date=$DATE_FINAL
declare -a max_month=(0 31 28 31 30 31 30 31 31 30 31 30 31)
eval $(echo $date|sed 's!\(....\)\(..\)\(..\)!year=\1;month=\2;day=\3!')
(( year4=year%4 ))
(( year100=year%100 ))
(( year400=year%400 ))
if [ \( $year4 -eq 0 -a \
$year100 -ne 0 \) -o \
$year400 -eq 0 ]
then
declare -a max_month=(0 31 28 31 30 31 30 31 31 30 31 30 31)
fi
day=$((day+1))
if [ $day -gt ${max_month[$month]} ] >| /wload/baot/home/baoted9/logs_bde27_conversion_1/ataa_display.logs 2>&1
then
day=1
month=$((month+1))
if [ $month -gt 12 ]
then
year=$((year+1))
month=1
fi
fi
if [ $month -eq "08" ] || [ $month -eq "09" ]
then
future_date_final=$(echo $year"$month"$day)
else
future_date_final=$(printf "%4.4d%2.2d%2.2d" $year $month $day)
fi
echo "this is your final business date $future_date_final"
It calculates the date correctly however throws an error at the end of the code as below -
line 79: 08: value too great for base (error token is "08")
It just looks too ugly, not sure how to remove it as otherwise code is working fine, tried redirecting it to a log file still appearing.
Also, I am facing issue with below code for a plain cd command with code highlighted in red -
echo "pset date $Param_date_1"
cd /wload/baot/home/baotasa0/sandboxes_finance/ext_ukba_bde/pset >| /wload/baot/home/baoted9/logs_bde27_conversion_1/at_display.logs 2>&1
sh UKBA_publish.sh UKBA $Param_date_1 3 >| /wload/baot/home/baoted9/logs_bde27_conversion_1/ate_display.logs 2>&1
Error is -
./auto2.sh: line 190: syntax error: unexpected end of file
The problem is, that if you're using $((val+1)) where $val is beginning with a 0, then bash isn't operating on decimal base but on octal base. Since 08 isn't a valid number in octal base, it complains.
You can force decimal representation by writing
$((10#$val+1))
Please note, that by doing so, any preceding zeros are removed. If you need to have a representation with preceding zero, just use
val=$(printf '%02d' $((10#$val+1)))
For reference, see the bash manual:
Constants with a leading 0 are interpreted as octal numbers. A leading ‘0x’ or ‘0X’ denotes hexadecimal. Otherwise, numbers take the form [base#]n, where the optional base is a decimal number between 2 and 64 representing the arithmetic base, and n is a number in that base. If base# is omitted, then base 10 is used. When specifying n, the digits greater than 9 are represented by the lowercase letters, the uppercase letters, ‘#’, and ‘_’, in that order. If base is less than or equal to 36, lowercase and uppercase letters may be used interchangeably to represent numbers between 10 and 35.
P.S.: This is a very good article about falsehoods programmers believe about time I had to read myself. If possible, try to use date -d instead of doing manual date arithmetic. You can use for instance date -d "+5 days" +%d.%m.%Y to get the date in five days.
Related
i'am currently trying to count +1 to a variable which could contain either
02
15
i use the following line to count one up, but the output seems not to be two digit. So if 02 is the $version and count one up, it will give the output "3" and not "03". Sure if its already two digit, it works.
$((echo $version) + 01))
how can I get bash to count with two digits and output it correctly?
Use printf to format a number. Also, note that 08 fails in numeric expressions, as numbers starting with 0 are interpreted as octal.
#!/bin/bash
shopt -s extglob
for n in 02 08 15 001; do
i=${n##+(0)} # Remove leading zeros. Needs the extglob.
length=${#n}
printf "%0${length}d\\n" $((i + 1))
done
We have a server which we use to run Selenium tests with our extension (for Chrome and Firefox). We run the Selenium tests every 2 hours. I want to run different tests after 14:00 than before 14:00. I have this variable:
start_hour=`TZ='Asia/Tel_Aviv' date +"%H"`
And I know how to compare it to a specific hour, such as 08:00 AM (it's a string):
if [ "$start_hour" = "08" ]; then
...
fi
But how do I check if this variable shows an hour after 14:00 (including 14:00), or before 14:00? Can I compare strings in bash and how? I just want to check if $start_hour is >= "14", or not?
Is the answer different if I want to check after 08:00 AM or before?
To perform greater or less-than comparisons, the test operations -gt, -le, -ge, and -le exist.
start_hour=$(TZ='Asia/Tel_Aviv' date '+%k')
[ "$start_hour" -ge 8 ]
Note the use of %k vs %H, and 8 vs 08 -- a leading 0 can prevent your numbers from being interpreted as decimal.
Similarly, in native bash syntax, for a numeric comparison:
start_hour=$(TZ='Asia/Tel_Aviv' date '+%k')
(( start_hour >= 8 ))
If your shell is bash, and not /bin/sh, and you truly do want an ASCII-sort string comparison, you can use > and < inside of [[ ]]:
start_hour=$(TZ='Asia/Tel_Aviv' date '+%H')
[[ $start_hour > 08 || $start_hour = 08 ]]
When running this part of my bash script am getting an error
Script
value=0
for (( t=0; t <= 4; t++ ))
do
d1=${filedates[$t]}
d2=${filedates[$t+1]}
((diff_sec=d2-d1))
SEC=$diff_sec
compare=$((${SEC}/(60*60*24)))
value=$((value+compare))
done
Output
jad.sh: line 28: ((: 10#2014-01-09: value too great for base (error token is "09")
jad.sh: line 30: /(60*60*24): syntax error: operand expected (error token is "/(60*60*24)")
d1 and d2 are dates in that form 2014-01-09 and 2014-01-10
Any solution please?
Prepend the string "10#" to the front of your variables. That forces bash to treat them as decimal, even though the leading zero would normally make them octal.
What are d1 and d2? Are they dates or seconds?
Generally, this error occurs if you are trying to do arithmetic with numbers containing a zero-prefix e.g. 09.
Example:
$ echo $((09+1))
-bash: 09: value too great for base (error token is "09")
In order to perform arithmetic with 0-prefixed numbers you need to tell bash to use base-10 by specifying 10#:
$ echo $((10#09+1))
10
As others have said, the error results from Bash interpreting digit sequences with leading zeros as octal numbers. If you have control over the process creating the date values and you're using date, you can prefix the output format string with a hyphen to remove leading zero padding.
Without prefixing date format with hyphen:
$ (( $(date --date='9:00' +%H) > 10 )) && echo true || echo oops
-bash: ((: 09: value too great for base (error token is "09")
oops
With prefixing date format with hyphen:
$ (( $(date --date='9:00' +%-H) > 10 )) && echo true || echo oops
true
From the date man page:
By default, date pads numeric fields with zeroes. The following
optional flags may follow '%':
- (hyphen) do not pad the field
d1 and d2 are dates in that form 2014-01-09 and 2014-01-10
and then
((diff_sec=d2-d1))
What do you expect to get? ((diffsec=2014-01-09-2014-01-10)) ??
You need to convert the dates to seconds first:
d1=$( date -d "${filedates[$t]}" +%s )
d2=$( date -d "${filedates[$t+1]}" +%s )
(( compare = (d2 - d1) / (60*60*24) ))
(( value += compare ))
Posting some tips here related to the title of this question, but not directly related to the details of the original question. I realize that's a bit controversial action on Stack Overflow, however these related questions:
convert octal to decimal in bash [duplicate]
Value too great for base (error token is "08") [duplicate]
point to this one, and yet they are closed and hence, I could not post this answer there. Therefore, this seemed like a logical place (at least to me) to post this information that may help others in a similar situation, especially new-to-BaSH programmers.
An alternative approach to ensuring a number is treated as a 10-base integer is to use printf. This command instructs printf to treat $num as an integer and round it to 0 decimal places.
num="$(printf "%.0f" "$num")"
Or, if you want to also ensure there are no non-numeric characters in the string, you can do this:
num="$(printf "%.0f" "${num//[!0-9]/}")"
Both commands will strip out leading zeroes and round decimal values to the nearest whole number. Note the first (simpler) solution works with negative numbers, but the second does not (it will always return absolute value).
Note that printf rounds down, meaning .01 to 0.5 is rounded down to 0, while .51 to .99 is rounded up to 1. Basically, the difference between rounding up versus down in this case is that printf rounds down 0.5 and any below. I mention this because 0.5 rounded up is a more common practice.
Now, addressing the OP's specific scenario.... Combining printf with awk allows arithmetic expressions not possible with printf alone.
This
compare=$((${SEC}/(606024)))
could be alternatively be expressed as
compare=$(awk -v sec=$SEC 'BEGIN { print int(sec/(60*60*24))}')
or
compare="$(printf "%.0f" "$(awk "BEGIN { print ( $SEC / ( 60 * 60 * 24 ) ) }")")"
Meanwhile,
value=$((value+compare))
Could be calculated as
value="$(printf "%.0f" "$(awk "BEGIN { print ( $value + $compare ) }")")"
You don't need the $ and the {} in an arithmetic expansion expression. It should look like this:
compare=$((SEC/(60*60*24)))
For 'mm' and 'dd' values in dates, I use this trick:
mm="1${date:5,2}" # where 5 is the offset to mm in the date
let mm=$mm-100 # turn 108 into 8, and 109 into 9
This question is related to the thread here:
Today's date, minus X days in shell script
But because I'm now manipulating the variable, I started another thread.
As described above, I need to get today's date minus 200 days, with the Year, Month, and Day in separate variables (in this question I'll use 200, though in the other it's 222). However, I need to represent January as 0, February as 1 (or 01), March as 2 (or 02), etc... I tried this:
MONTHS200=$(date -j -v-200d -v-1m +"%m")
if ${MONTHS200}=01; then
${MONTH200}=0
else ${MONTHS200}=${MONTH200}
fi
But I get the error ./update_newdateformat.sh: line 20: 12=01: command not found ./update_newdateformat.sh: line 23: 12=: command not found The -v-1m works for all months except January, because it goes to 12, instead of 0
Here's how to shift all the month number down by 1 n your script:
MONTHS200=$(date -j -v-320d +"%m")
# Remove leading zero if there is one, so it doesn't cause problems later
MONTHS200=${MONTHS200#0}
MONTHS200=$((MONTHS200-1))
Here is how to use if and = (assignment) syntax in shell:
if [[ "${MONTHS200}" == "01" ]]; then
MONTHS200="0"
else
MONTHS200=${AnotherVariable}
fi
Note that for numerical comparisons, you need to use:
-eq instead of ==
-ne instead of !=
-lt instead of <
-le instead of <=
-gt instead of >
-ge instead of >=
For example:
if [[ "${MONTHS200}" -eq 1 ]]; then
I would take advantage of bash features (I assume OSX bash is recent enough -- I might be wrong). You only need to call date once with
read year month day < <(date -j -v-200d +"%Y %m %d")
month=$(( 10#$month - 1 ))
You avoid the octal issue by forcing bash to use base-10
In a text file, test.txt, I have the next information:
sl-gs5 desconnected Wed Oct 10 08:00:01 EDT 2012 1001
I want to extract the hour of the event by the next command line:
hour=$(grep -n sl-gs5 test.txt | tail -1 | cut -d' ' -f6 | awk -F ":" '{print $1}')
and I got "08". When I try to add 1,
14 echo $((hour+1))
I receive the next error message:
./test2.sh: line 14: 08: value too great for base (error token is "08")
If variables in Bash are untyped, why?
See ARITHMETIC EVALUATION in man bash:
Constants with a leading 0 are interpreted as octal numbers.
You can remove the leading zero by parameter expansion:
hour=${hour#0}
or force base-10 interpretation:
$((10#$hour + 1))
what I'd call a hack, but given that you're only processing hour values, you can do
hour=08
echo $(( ${hour#0} +1 ))
9
hour=10
echo $(( ${hour#0} +1))
11
with little risk.
IHTH.
You could also use bc
hour=8
result=$(echo "$hour + 1" | bc)
echo $result
9
Here's an easy way, albeit not the prettiest way to get an int value for a string.
hour=`expr $hour + 0`
Example
bash-3.2$ hour="08"
bash-3.2$ hour=`expr $hour + 0`
bash-3.2$ echo $hour
8
In Short: In order to deal with "Leading Zero" numbers (any 0 digit that comes before the first non-zero) in bash
- Use bc An arbitrary precision calculator language
Example:
a="000001"
b=$(echo $a | bc)
echo $b
Output: 1
From Bash manual:
"bc is a language that supports arbitrary precision numbers with interactive execution
of statements. There are some similarities in the syntax to the C programming lan-
guage. A standard math library is available by command line option. If requested, the
math library is defined before processing any files. bc starts by processing code from
all the files listed on the command line in the order listed. After all files have
been processed, bc reads from the standard input. All code is executed as it is read.
(If a file contains a command to halt the processor, bc will never read from the standard input.)"
Since hours are always positive, and always 2 digits, you can set a 1 in front of it and subtract 100:
echo $((1$hour+1-100))
which is equivalent to
echo $((1$hour-99))
Be sure to comment such gymnastics. :)
The leading 0 is leading to bash trying to interpret your number as an octal number, but octal numbers are 0-7, and 8 is thus an invalid token.
If I were you, I would add some logic to remove a leading 0, add one, and re-add the leading 0 if the result is < 10.
How about sed?
hour=`echo $hour|sed -e "s/^0*//g"`