如何在UNIX Shell中将字符串转换为整数

65

我有d1="11"d2="07"。我想将d1d2转换为整数并执行d1-d2。在UNIX中该怎么做?

d1 - d2目前对我返回结果"11-07"


4
你是想通过命令行还是编程来实现这个功能? - Levon
5个回答

92

标准解决方案:

 expr $d1 - $d2

你也可以这样做:

echo $(( d1 - d2 ))

但请注意,这将把07视为八进制数字!(因此077相同,但01010不同)。


1
鉴于Bash具有指定数字基数的语法,为什么不在这里演示它(尽管存在可移植性警告)? - Charles Duffy
4
你可以进行进制转换:echo $((d1-10#$d2))注意:这与 echo $((d1-10#d2)) 不同。 - Luchostein
考虑到Bash不是标准且无法保证在所有目标上都可用...既然他们没有要求使用Bash,那么考虑跳过Bash特有的语法。 - Svartalf
如果您的变量可以保存非整数字符串,或者如果您正在使用选项nounseterrexit(您应该始终使用),请小心。我在我的答案https://dev59.com/1Wgu5IYBdhLWcg3wgnVh#59781257中为不同情况编写了许多测试。 - Bruno Bronosky
你应该谨慎处理非整数值,并且使用 nounset 通常是个好主意。但是你绝对不应该使用 errexit - William Pursell

24
任何一个都可以在 shell 命令行中使用。但是,bc 可能是最简单的解决方案。
使用 bc
$ echo "$d1 - $d2" | bc

使用 awk

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

使用 perl:

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

使用 Python

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

所有返回

4

1
不确定exprawkbcdc是否存在错误或特性,但是所有这些工具都将d1=09作为十进制值9处理。然而,perlpython将其视为语法错误。对于d1=010的情况,您会得到不同的结果。 - William Pursell
@WilliamPursell 有趣..我得研究一下,有用的信息,谢谢。 - Levon
4
你可以直接使用纯Bash来完成:echo $((d1-10#$d2)) - Luchostein

10

不仅限于原问题的答案

这个问题的标题吸引了很多人,所以我决定为其他人回答这个问题,因为原问题描述的情况太过狭窄。

简短总结

我最终决定编写一个函数。

  1. 如果你想在非整数的情况下得到 0
int(){ printf '%d' ${1:-} 2>/dev/null || :; }
  1. 如果你想在非整数情况下得到[空字符串]
int(){ expr 0 + ${1:-} 2>/dev/null||:; }
  • 如果你想要找到第一个整数或者空字符串:
  • int(){ expr ${1:-} : '[^0-9]*\([0-9]*\)' 2>/dev/null||:; }
    

    如果您想找到第一个整数或0:
    # This is a combination of numbers 1 and 2
    int(){ expr ${1:-} : '[^0-9]*\([0-9]*\)' 2>/dev/null||:; }
    

    如果您想在非整数上获得一个非零状态码,请移除 ||: (也称作 or true)但是保留 ;

    测试

    # 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
    )
    

    测试输出

    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
    

    注意

    我深入研究这个问题,是因为被接受的答案set -o nounset(也称为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
    

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

    这应该会有所帮助。


    这实际上与已接受的答案相同,即 d=$(( d1 - d2 )); echo "$d"(但需要注意的是,以 0 为前缀的数字会被解释为_八进制_)。 - mklement0

    -10
    使用这个:
    #include <stdlib.h>
    #include <string.h>
    
    int main()
    {
        const char *d1 = "11";
        int d1int = atoi(d1);
        printf("d1 = %d\n", d1);
        return 0;
    }
    

    等等。


    7
    隔离意味着 Linux Shell。 - Lukasz Czerwinski

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接