3

I want to pass a numeric value to a bash script, which could be either an absolute integer value (0-100) or a relative "delta" like "+10" or "-50".

How would I parse $1 to check for + or - and grab the remaining text as a value?

1

Base on @frams's idea, we can make the answer shorter:

sign=${1//[[:digit:]]}
value=${1//[^[:digit:]]}

${1//[[:digit:]]} deletes digit part of ${1}

${1//[^[:digit:]]} deletes non-digit part of ${1}

ref: Advanced Bash Scripting Guide by Mendel Cooper

  • Nice! Works with "+10" as well as "10+" etc. – Dan Jan 1 '18 at 19:25
  • 1
    Of course it would also catch "1+0" – David Z Jan 2 '18 at 0:54
  • 1
    @DavidZ In this case, I think we could use if [[ $1 -eq $1 ]]; then echo ${1//[^0-9]} ; fi to make sure $1 is a number, i.e. 10, +10, or -10 – harbinn Jan 2 '18 at 1:33
4

You can use ${var:offset:length} to extract part of a variable and compare the result of this as needed. If you omit the length part the remainder of the string starting from offset will be taken.

if [[ ${1:0:1} == "+" ]]; then
    is_negative=false
    is_delta=true
    arg=${1:1}
elif [[ ${1:0:1} == "-" ]]; then
    is_negative=true
    is_delta=true
    arg=${1:1}
else
    is_negative=false
    is_delta=false
    arg=$1
fi
  • Code snippet is not a very good quality answer, I suggest to use also human language. – peterh Jan 1 '18 at 14:14
2

There is a trick to do this

sign=`echo $1|sed -e 's/[0-9]//g'`;
 num=`echo $1|sed -e 's/[+-]//g'`;
echo $sign;  # gets + - or empty
echo $num;   # gets the number

sed -e 's/[0-9]//g' delete all digits, + - remains

sed -e 's/[+-]//g' delete + -, digits remains

works for all cases including 0, +0, -0.

1

With posix shell

unset -v sign value
value="${1#[-+]}"
if [ "$value" -eq "$value" 2> /dev/null ];then
  [ "$1" != "$value" ] && sign="${1%$value}"
  echo "sign = $sign value = $value"
else
  echo "not a number $1"
fi
1

For Bash, I would use the syntax ${var:offset:length} like in patrix's answer, but with another approach. You should compute your base value before.

For example (the value is relative to the current hour):

[[ "$1" =~ ^[+-]?(0|[1-9][0-9]*)$ ]] || exit 2

base="$(date +"%-H")"

case "${1:0:1}" in
"+")
    value="${1:1}"                                                                                                                 
    value=$((base + value))
    ;;
"-")
    value="${1:1}"                                                                                                                 
    value=$((base - value))
    ;;
*)
    value="$1"
    ;;
esac

((value < 0)) || ((value > 23)) && exit 2

echo "Task planned for ${value}h"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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