6
\$\begingroup\$

This is a simple bash script (hopefully with a little style as well) for converting temperatures from Fahrenheit to Celsius and vice versa.

#!/bin/bash

re='^-*[0-9]+([.][0-9]+)?$'
while true
do
    printf "\n\e[1;39mWhat is the temperature you wish to convert?\e[0m\n\n\t"
    read -p "Enter a numeric value  " n
    while [[ ! $n =~ $re ]]
    do 
        printf '\t\e[3;3;31mInvalid selection\n\t\e[3;3;31m\e[0m\n\t'
        read -p "Enter a numeric value " n

    done
    echo
    printf "\n\tIs this \e[1;39m(F)\e[0mahrenheit or \e[1;39m(C)\e[0melsius\e[0m?\n\n"
    while true
    do
        read -sn 1 cf
        case $cf in 
            [Ff] ) echo "$n""˚F = $(( ($n - 32) * 5 / 9 ))˚C"; break;;
            [cC] ) echo "$n""˚C = $(( $n * 9 / 5 + 32 ))˚F"; break;;
            * ) printf '\t\e[3;3;31mInvalid selection\n\e[3;3;31m\e[0m'
            printf '\tChoose \e[1;39m(F)\e[0mahrenheit or \e[1;39m(C)\e[0melsius\e[0m\n\n'
        esac
    done
    echo
    while true
    do
        printf "\tPress (1) to convert another\n\tpress (2) to quit"
        read -sn1 bye ; printf "\n\n"
        case $bye in
            [1] ) 
            break
            ;;
            [2] ) printf "Now exiting" && exit
            ;;
            * ) 
            printf '\t\e[3;3;31mInvalid selection\n\e[3;3;31m\e[0m'
            ;;
        esac
    done
done

Is there anything I could do to improve upon this? I am also curious if this would be portable / POSIX compliant. Everyone always says to use printf if portability is a concern. Is echo not portable?

\$\endgroup\$
1
  • \$\begingroup\$ Now, also convert to/from Kelvin, Rankine, and Felsius. \$\endgroup\$ Feb 10, 2020 at 14:43

3 Answers 3

9
\$\begingroup\$

I don't know enough bash to offer you a technical review.

But, more philosophically: Have you considered writing this more in the style of a "program" than a "Press 1 to convert another"-type interface? Like:

~$ temperature c 30
86 ˚F
~$ temperature F 105
40 ˚C

My reasoning is that the above is easier and more flexible to use programatically, you don't need to write input loops at all and you can condense down to your core functionality:

#!/bin/bash

case $1 in
    [Ff] ) echo "$(( ($2 - 32) * 5 / 9 )) ˚C"; break;;
    [cC] ) echo "$(( $2 * 9 / 5 + 32 )) ˚F"; break;;
    *    ) echo "Usage: $0 c|f num"
esac
\$\endgroup\$
3
  • \$\begingroup\$ I'm not sure I understand what you mean. Bash is really the only thing I know and I'm still very much a beginner. Without the loops how would I determine whether or not valid data is being entered? \$\endgroup\$
    – I0_ol
    Sep 6, 2016 at 2:15
  • 1
    \$\begingroup\$ @user556068 Sorry for being unclear! You can still do the validation -- but then just abort if given invalid data. Give the user a usage message, quit, and let them try again. Or, if the user wants to convert another temperature, let them just invoke your script/command again in their shell, with new parameters. Think about ls: it does one thing and quits. If you provide a bad folder name, it tells you so and quits. To ls another folder, you call it again in your shell. That's the approach I'm suggesting. \$\endgroup\$
    – BenC
    Sep 8, 2016 at 2:32
  • \$\begingroup\$ Now I understand what you're saying. Thank you for clarifying. I have made changes to my script, taking out the loops as you suggested. \$\endgroup\$
    – I0_ol
    Sep 15, 2016 at 22:07
4
\$\begingroup\$

The * operator matches zero or more of the preceding character. As such, your program will accept ----1 as input. You want ?, as this will only accept the one possible negative sign.

As reference: tldp.org....

\$\endgroup\$
1
  • \$\begingroup\$ Thank you. I have updated my script (not this question). Your answer also made me take a closer look at things and I realized that while re='^-?[0-9]+([.][0-9]+)?$' allows for decimals, my current way of calculating them does not. So now the instead of echo $(( $n * ... )) I am using echo "scale=2; ($n-32)*5/9" | bc. \$\endgroup\$
    – I0_ol
    Sep 6, 2016 at 2:24
0
\$\begingroup\$
\e[1;39m
\e[0m
\e[3;3;31m
\e[3;3;31m\e[0m

Do you know (without looking up?) what all these control sequences mean? Will you, in six months' time when you next need to update the program?

Not every terminal accepts ANSI control sequences (although that has become the most common control set).

Address both these problems by generating the correct codes using tput.

Or just stop shouting at your users.

\$\endgroup\$

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