获得"数字参数需要"使用算术运算运行脚本时

时间:2017-08-18 21:28:54

标签: shell sh

我试图编写一个函数,该函数应该将形式为hr:min:sec,ms(即15:41:47,757)的时间戳转换为毫秒。功能如下:

#!/bin/sh
mili () {

    hr=$(echo "$1" | cut -c1-2)
    echo "hr is: " $hr
    min=$(echo "$1" | cut -c4-5)
    echo "min is: " $min
    sec=$(echo "$1" | cut -c7-8)
    echo "sec is: " $sec
    ms=$(echo "$1" | cut -c10-12)
    echo "ms is: " $ms
    total=$(($hr \* 3600 + $min \* 60 + $sec) \* 1000 + $ms)

    return "$total"
    #echo "Result is: "$total" "
}

mili $1

然而,当我运行它时:

./ mili.sh" 15:41:47,757"

我收到以下输出消息:

./mili.sh: command substitution: line 15: syntax error near unexpected token 
`\*'
./mili.sh: command substitution: line 15: `($hr \* 3600 + $min \* 60 + $sec) 
\* 1000 + $ms'
./mili.sh: line 17: return: : numeric argument required

我尝试过使用和不使用单引号,双引号和反引号的expr变体,但似乎永远无法计算算术。我可以确认这样一个简单的命令:expr 2 * 3但是当我尝试在我的脚本中使用类似的东西时它会失败。

如何才能简单地计算我的表达式?

3 个答案:

答案 0 :(得分:5)

在算术内部,*不需要转义。还有一些括号丢失了。因此,替换:

total=$(($hr \* 3600 + $min \* 60 + $sec) \* 1000 + $ms)

使用:

total=$((($hr * 3600 + $min * 60 + $sec) * 1000 + $ms))

替代

可以简化代码,无需多次调用cut

mili() {
    IFS=':,' read hr min sec ms <<<"$1"
    echo "hr is: " $hr
    echo "min is: " $min
    echo "sec is: " $sec
    echo "ms is: " $ms
    total=$((($hr * 3600 + $min * 60 + $sec) * 1000 + $ms))
    echo "Total=$total"
    return "$total"
}

除此之外:Bash算术和美元符号

在bash算术上下文中,变量前的美元符号是可选的。例如:

$ a=1; echo "$((1 + a)) and $((1+ $a))"
2 and 2

虽然某些样式指南建议在算术上下文中省略$,但有一个关键区别。正如Chepner在评论中指出的那样,对未定义变量的处理是非常不同的:

$ unset a
$ echo $((1 + $a))
bash: 1 + : syntax error: operand expected (error token is "+ ")
$ echo $((1 + a))
1

总之:

  1. 如果您希望未定义的变量默认为零,则省略$

  2. 如果您希望将未定义的变量替换为空,可能导致表达式无效,请包含$

  3. 在shell函数mili中,未定义的变量hrmin等会指示代码错误,我们可能需要一条错误消息来警告我们,我们会想要加入$。在默认值为零合理的其他情况下,我们不会忽略$是正确的。

答案 1 :(得分:3)

另外两点:

  1. 不要return "$total":返回值是0到255之间的int。您需要echo "$total"

  2. 当小时/分钟/秒为0809时,您将遇到错误 - bash将前导零的数字视为八进制,而8和9是无效的八进制位数。

    $ mili 11:22:09,456
    hr is:  11
    min is:  22
    sec is:  09
    ms is:  456
    bash: (11 * 3600 + 22 * 60 + 09: value too great for base (error token is "09")
    
  3. 我写道:

    mili () {     
        IFS=":,." read -r hr min sec ms <<<"$1"
        echo "hr is:   $hr" >&2
        echo "min is:  $min" >&2
        echo "sec is:  $sec" >&2
        echo "ms is:  $ms" >&2
        echo "$(( ((10#$hr * 60 + 10#$min) * 60 + 10#$sec) * 1000 + 10#$ms ))"
    }
    

    10#迫使基数为10的数字

    然后

    $ ms=$(mili 11:22:09.456)
    hr is:   11
    min is:  22
    sec is:  09
    ms is:  456
    
    $ echo $ms
    40929456
    

答案 2 :(得分:1)

这是一个疯狂的选择:

$ mili () {
    IFS=., read -r time ms <<<"$1"
    ms3=$(cut -c 1-3 <<<"${ms}000")
    echo "$(date -u -d "1970-01-01 $time" +%s)$ms3"
}

$ mili 15:41:47,757
56507757

$ mili 15:41:47,75
56507750

$ mili 15:41:47
56507000