0

I wrote this bash script that runs multiple timers. The problem is my array stores whole numbers:

|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1

But the other user's LC_NUMERIC is setup as it_IT.UTF-8 and their bash array is very different:

1,000000|0,000000|0,000000|0,000000|0,000000|0,000000|0,000000|0,000000|0,000‌​000|0,000000|0,000000|0,000000|0,000000|0,000000|0,000000|0,000000|1,000000

This causes my script to break when testing if an array entry is greater than zero:

iActiveTimersCount=0
for ((i=0; i<MAX_TIMERS; i++)); do
    if [[ ${aDuration[i]} -gt 0 ]] ; then
        (( iActiveTimersCount++ ))
        iAllTimersSaveSec=$(( iAllTimersSaveSec + ${aDuration[i]} ))
    fi
done

Command line tests

These tests can confirm what bash is "thinking":

$ if [[ 30,000000 -gt 0 ]]; then echo TRUE ; else echo FALSE ; fi
FALSE

$ if [[ 30.000000 -gt 0 ]]; then echo TRUE ; else echo FALSE ; fi
bash: [[: 30.000000: syntax error: invalid arithmetic operator (error token is ".000000")
FALSE

Other Info

I've tried (via chat) getting other user to use export LC_ALL=C and export LC_NUMERIC="en_US.UTF-8" all to no avail.

How can bash be forced to use whole numbers (integers)?


Solution

The problem was YAD (Yet Another Dialog) storing numbers with decimal precision in Italy but not in North America. The solution was to use:

    # When LC_NUMERIC=it_IT-UTF8 30 seconds can be `30,000000` or
    # `30.000000` which breaks bash tests for `-gt 0`.
    # Search and replace ".000000" or ",000000" to null
    sed -i 's/[,.]000000//g' "$res1"
    sed -i 's/[,.]000000//g' "$res2"

closed as unclear what you're asking by Kusalananda, Isaac, Rui F Ribeiro, Timothy Martin, G-Man Jun 5 '18 at 2:16

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • That isn't floating point. I take it your question is essentially "How do I remove these commas?" – Jesse_b Jun 2 '18 at 20:52
  • Remove all ,000000? – Cyrus Jun 2 '18 at 20:59
  • @Cyrus for other nations they might need to remove .000000 or even different number of floating points I guess, ie .00. – WinEunuuchs2Unix Jun 2 '18 at 22:06
  • In some locales, a comma is used instead of a period to indicate a decimal point. In that case, the Bash script would be attempting to process real numbers instead of integers. However, Bash only supports integers. – fpmurphy Jun 3 '18 at 6:18
  • Why don't you just set LC_ALL or LC_NUMERIC to the appropriate locale string in your script? – Kusalananda Jun 3 '18 at 6:35
1

What about this:

iActiveTimersCount=0
for ((i=0; i<MAX_TIMERS; i++)); do
    dur=$(awk -F, '{print $1}' <<<"${aDuration[i]}")
    if [[ $dur -gt 0 ]] ; then
        (( iActiveTimersCount++ ))
        iAllTimersSaveSec=$(( iAllTimersSaveSec + dur ))
    fi
done
  • Well that is a great brute force answer. I was hoping for something universal for LC / locale to work across all applications and scripts for user. But I'll try this method in the time being and report back. Thank you. – WinEunuuchs2Unix Jun 2 '18 at 21:24
  • I copied the other user's test data to my own configuration file ~/.multi-timer but the problem I have is when I run the script bash is automatically converting their floating point number 30.000000 to 30 when populating the array ${aDuration[i]} when reading the file. This makes it impossible for me to run the awk command on my system because it isn't broken in the first place. – WinEunuuchs2Unix Jun 2 '18 at 22:02
  • the awk command should work fine in either scenario. echo 30 | awk -F, '{print $1}' should return 30 – Jesse_b Jun 2 '18 at 23:10
  • Depending on locale, , can be used as decimal point. So variable for 30 seconds could be 30,000000 or 30.000000. I'm leaning towards fixing values when array is read, rather than each program usage. For example: while [[ $iCurrTimerElapsedSec -lt $iCurrTimerSaveSec ]]; do and sleep $iLastSleepSec lines might also have to be changed. This indicates fixing variables as they are read from disk using var=${var%%.*} followed by var=${var%%,*}. As I said I have to get other user to test, because on my system bash automatically converts floating point to integer / whole number. – WinEunuuchs2Unix Jun 2 '18 at 23:18
  • You could handle either in one operation with something like ${var%%[.,]*} – Jesse_b Jun 2 '18 at 23:21

Not the answer you're looking for? Browse other questions tagged or ask your own question.