68

I have d1="11" and d2="07". I want to convert d1 and d2 to integers and perform d1-d2. How do I do this in UNIX?

d1 - d2 currently returns "11-07" as result for me.

1
  • 4
    Did you want this from the command line or programatically? Commented Jun 29, 2012 at 20:57

5 Answers 5

95

The standard solution:

 expr $d1 - $d2

You can also do:

echo $(( d1 - d2 ))

but beware that this will treat 07 as an octal number! (so 07 is the same as 7, but 010 is different than 10).

Sign up to request clarification or add additional context in comments.

5 Comments

Given that bash has syntax for specifying numeric base, why not demonstrate it here (albeit with a portability caveat)?
You can base-convert: echo $((d1-10#$d2)) Beware: it is not the same than echo $((d1-10#d2))
Given that bash is NOT the standard and isn't guaranteed on all targets...how about skipping the bash-isms since they didn't ask how to do it in bash...?
Be careful if your variables can hold non-integer strings or if you are using options nounset and errexit (which you should always use). I have written many tests for different scenarios in my answer stackoverflow.com/a/59781257/117471
You should definitely be wary of non-integer values, and using nounset is often a good idea. But you should never use errexit.
24

Any of these will work from the shell command line. bc is probably your most straight forward solution though.

Using bc:

$ echo "$d1 - $d2" | bc

Using awk:

$ echo $d1 $d2 | awk '{print $1 - $2}'

Using perl:

$ perl -E "say $d1 - $d2"

Using Python:

$ python -c "print $d1 - $d2"

all return

4

3 Comments

Not sure if it is a bug or a feature of expr, awk, bc, and dc, but all of those tools handle the case d1=09 by treating 09 as the decimal value 9. However, perl and python treat it as a syntax error. You get different results for the case d1=010
@WilliamPursell Interesting .. I'll have to look into this, useful info, thanks.
You can do it straight away with pure Bash: echo $((d1-10#$d2))
11

An answer that is not limited to the OP's case

The title of the question leads people here, so I decided to answer that question for everyone else since the OP's described case was so limited.

TL;DR

I finally settled on writing a function.

  1. If you want 0 in case of non-int:
int(){ printf '%d' ${1:-} 2>/dev/null || :; }
  1. If you want [empty_string] in case of non-int:
int(){ expr 0 + ${1:-} 2>/dev/null||:; }
  1. If you want find the first int or [empty_string]:
int(){ expr ${1:-} : '[^0-9]*\([0-9]*\)' 2>/dev/null||:; }
  1. If you want find the first int or 0:
# This is a combination of numbers 1 and 2
int(){ expr ${1:-} : '[^0-9]*\([0-9]*\)' 2>/dev/null||:; }

If you want to get a non-zero status code on non-int, remove the ||: (aka or true) but leave the ;

Tests

# Wrapped in parens to call a subprocess and not `set` options in the main bash process
# In other words, you can literally copy-paste this code block into your shell to test
( set -eu;
    tests=( 4 "5" "6foo" "bar7" "foo8.9bar" "baz" " " "" )
    test(){ echo; type int; for test in "${tests[@]}"; do echo "got '$(int $test)' from '$test'"; done; echo "got '$(int)' with no argument"; }

    int(){ printf '%d' ${1:-} 2>/dev/null||:; };
    test

    int(){ expr 0 + ${1:-} 2>/dev/null||:; }
    test

    int(){ expr ${1:-} : '[^0-9]*\([0-9]*\)' 2>/dev/null||:; }
    test

    int(){ printf '%d' $(expr ${1:-} : '[^0-9]*\([0-9]*\)' 2>/dev/null)||:; }
    test

    # unexpected inconsistent results from `bc`
    int(){ bc<<<"${1:-}" 2>/dev/null||:; }
    test
)

Test output

int is a function
int ()
{
    printf '%d' ${1:-} 2> /dev/null || :
}
got '4' from '4'
got '5' from '5'
got '0' from '6foo'
got '0' from 'bar7'
got '0' from 'foo8.9bar'
got '0' from 'baz'
got '0' from ' '
got '0' from ''
got '0' with no argument

int is a function
int ()
{
    expr 0 + ${1:-} 2> /dev/null || :
}
got '4' from '4'
got '5' from '5'
got '' from '6foo'
got '' from 'bar7'
got '' from 'foo8.9bar'
got '' from 'baz'
got '' from ' '
got '' from ''
got '' with no argument

int is a function
int ()
{
    expr ${1:-} : '[^0-9]*\([0-9]*\)' 2> /dev/null || :
}
got '4' from '4'
got '5' from '5'
got '6' from '6foo'
got '7' from 'bar7'
got '8' from 'foo8.9bar'
got '' from 'baz'
got '' from ' '
got '' from ''
got '' with no argument

int is a function
int ()
{
    printf '%d' $(expr ${1:-} : '[^0-9]*\([0-9]*\)' 2>/dev/null) || :
}
got '4' from '4'
got '5' from '5'
got '6' from '6foo'
got '7' from 'bar7'
got '8' from 'foo8.9bar'
got '0' from 'baz'
got '0' from ' '
got '0' from ''
got '0' with no argument

int is a function
int ()
{
    bc <<< "${1:-}" 2> /dev/null || :
}
got '4' from '4'
got '5' from '5'
got '' from '6foo'
got '0' from 'bar7'
got '' from 'foo8.9bar'
got '0' from 'baz'
got '' from ' '
got '' from ''
got '' with no argument

Note

I got sent down this rabbit hole because the accepted answer is not compatible with set -o nounset (aka set -u)

# This works
$ ( number="3"; string="foo"; echo $((number)) $((string)); )
3 0

# This doesn't
$ ( set -u; number="3"; string="foo"; echo $((number)) $((string)); )
-bash: foo: unbound variable

Comments

1
let d=d1-d2;echo $d;

This should help.

1 Comment

This is effectively the same as d=$(( d1 - d2 )); echo "$d", as covered by the accepted answer (with the same caveat that 0-prefixed numbers are interpreted as octals).
-10

Use this:

#include <stdlib.h>
#include <string.h>

int main()
{
    const char *d1 = "11";
    int d1int = atoi(d1);
    printf("d1 = %d\n", d1);
    return 0;
}

etc.

1 Comment

qwarentine meant Linux shell.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.