涉及简单数学的 bash 脚本

涉及简单数学的 bash 脚本

我是脚本新手,我想编写一个脚本来处理它的参数,如下所示:

$ tandenstokers "| | x|| | |+|"
|| x |||| + | = 9 (11 tandenstokers)

$ tandenstokers \| \| x\|\| \| \|\+\|
|| x |||| + | = 9 (11 tandenstokers)

当我得到这个输出时:

$ tandenstokers \| \| x\|\| \| \|\+\|
|| x |||| + | = 0 (0 tandenstokers)

它的作用基本上是计算“棍子”的数量并对其进行简单的数学运算。结果输出为:1. 给定的参数,在 x 或 + 之前和之后只留有空格 2. 上例中的 9 是数学运算的结果。3. 11 是公式中使用的棍子数量(a + 和 ax 算作两根棍子)

到目前为止,我有以下代码,但我不明白我的错误在哪里:

#!/bin/bash

uitdrukking="$*"
size=${#uitdrukking}
mooi=$(echo "$uitdrukking" | sed -e 's/ //g' | sed -e 's/+/ + /g' | sed -e 's/x/ x /g')
x=0
y=1
a=1
n=0
m=0
sub=0
while [ $a -le $size ]
do
((a++))
    if [ ${uitdrukking:$x:$y} -eq "|" ]
        then
            ((n++))
            ((x++))
            ((y++))
            ((m++))
    elif [ ${uitdrukking:$x:$y} -eq "+" ]
        then
            ((x++))
            ((y++))
            m=$[ $m + 2 ]
    elif [ ${uitdrukking:$x:$y} -eq "x" ]
        then
        ((x++))
        ((y++))
        m=$[ $m + 2 ]
            while [ ${uitdrukking:$x:$y} -eq "|" ]
            do
                ((sub++))
                ((x++))
                ((y++))
                ((m++))
            done
            n=$[ $n * $sub ]
    else
        ((x++))
        ((y++))
    fi
    done
echo "$mooi = $n ($m tandenstokers)"

通过从头开始采用不同的方法解决了这个问题,得到了以下正确的代码:

#!/bin/bash
echo "$(echo "$*" | sed -e 's/ //g' | sed -e 's/+/ + /g' | sed -e 's/x/ x /g') = $(($(echo "$*" | sed -e 's/ //g' | sed -e 's/\(\|[^x+]*\)/(\1)/g' | sed -e 's/|/1 + /g' | sed -e 's/ + $//g' | sed -e 's/ + +/ + /g' | sed -e 's/ + x/ \* /g' | sed -e 's/ + \([^1]\)/\1/g' | sed -e 's/x/ \* /g'))) ($(($(echo "$*" | sed -e 's/ //g' | sed -e 's/|/1 p /g' | sed -e 's/ p $//g' | sed -e 's/x/2 p /g' | sed -e 's/+/2 p /g' | sed -e 's/p/+/g'))) tandenstokers)"

答案1

恐怕你的脚本不太容易理解。你的方法似乎过于复杂,无法理解它所要做的事情,而且由于你没有解释你认为脚本的每个部分的作用,因此理解起来并不容易。

也就是说,您的第一个问题(如果您包含错误消息,这个问题会很明显)是您使用了-eq进行词汇比较。您需要的是 ,=因为您匹配的是字符串,而不是数字。

下一个问题,从错误消息中再次显而易见的是

/home/terdon/scripts/foo.sh: line 15: [: too many arguments
/home/terdon/scripts/foo.sh: line 21: [: too many arguments
/home/terdon/scripts/foo.sh: line 26: [: too many arguments

这是因为各种${uitdrukking:$x:$y}扩展为包含空格的字符串,而不是单个字符。这可能是因为您在许多地方增加了各种计数器变量,因此您的速度$y>1非常快。我认为(但是,同样,我不能确定,因为您的问题实际上并没有解释您认为发生了什么)您误解了语法的工作原理。它不会从位置到位置${var:x:y}提取子字符串。它提取从位置开始的子字符串和varxyvarxy 长字符

编程的第一条规则任何语言是:当出现问题时,打印出所有变量. 十有八九,问题在于变量没有您认为它具有的值。

无论如何,这里有太多问题需要调试,从头开始会简单得多。例如,此脚本执行您想要的操作:

#!/bin/bash
mooi=$(echo "$*" | sed -e 's/ //g' | sed -e 's/+/ + /g' | sed -e 's/x/ x /g')
## I use fold to print one character at a time and then iterate
## over the resulting strings. 
while read char; do
    case $char in
        ## If this is a |, increment the total number of |
        ## found and the current number (until the next operator).
        "|")
            ((pipeNum++))
            ((totPipes++))
            ;;
        ## If this is an operator
        [+x])
            ## Change x to * for bc
            char=$(echo "$char" | tr 'x' '*')
            ## Increment the operator count by 2 as requested. 
            operators=$((operators + 2))
            ## Append the number of pipes so far and the 
            ## current operator to the $string variable. This 
            ## will hold the expression we'll give to bc. 
            string="$string $pipeNum $char"
            ## reset the pipeNum to 0 for the next operation. 
            pipeNum=0
            ;;
            ## Ignore all other cases. 
            "*")
                continue
                ;;
    esac
done < <(fold -w 1 <<<"$*")
## Add the last set. 
string="$string $pipeNum"
## Count the total
tandenstokers=$((totPipes + operators))
## Use bc to calculate
echo "$mooi = $(echo "$string" | bc) ($tandenstokers tandenstokers)"

要查看实际效果,请执行以下操作:

$ foo.sh '| | x | | | | + |'
|| x |||| + | = 9 (11 tandenstokers)
$ foo.sh \| \| x\|\| \| \|\+\|
|| x |||| + | = 9 (11 tandenstokers)

答案2

首先,回答你的问题,-eq是用于整数比较。用于=字符串。我认为这是你的主要错误。

一些忠告:

  • 默认引用变量(即"$n"不仅仅是$n)。在某些情况下可能需要取消引用它们,但这必须是例外
  • 总是用来set -u检测未初始化变量的使用
  • 将 sed 指令串联在一个 sed 命令中:sed -e 's/ //g; s/[+x]/ & /g;' 而不是三条命令管道sed -e 's/ //g' | sed -e 's/+/ + /g' | sed -e 's/x/ x /g'
  • 想想 shell(我知道当你学习 sh 时这很难):将代码分成小命令(函数),使用参数并利用 shell 解析器(for word in "$@")。在这里,我看到的是一个包含很多内容的大循环N++,你自己逐个字符地解析所有内容。

为了代码高尔夫的乐趣,这里是一个 sed + 算术评估中的计算部分:

tandenstokers()
{
    echo "$((
        $(sed -e '
            s/ //g
            s/[xX]/*/g
            s/|\+/\(&\)/g
            s/|/+1/g
        ' <<< "$*" )
    ))"
}

$ tandenstokers "| | x|| | |+|"
9

$ tandenstokers "|| + ||| - | X |||| / ||"
3

这是一个完全不同的解决方案。把它当作一种好奇心,不要放弃你的好奇心!我所做的只是将您的 tandenstokers 公式转换为常规算术公式,最后用 进行评估$(( ... ))

我留给你一个练习,计算一下公式中牙签的数量。

答案3

这是我针对该问题的解决方案:

#!/bin/bash
echo "$(echo "$*" | sed -e 's/ //g' | sed -e 's/+/ + /g' | sed -e 's/x/ x /g') = $(($(echo "$*" | sed -e 's/ //g' | sed -e 's/\(\|[^x+]*\)/(\1)/g' | sed -e 's/|/1 + /g' | sed -e 's/ + $//g' | sed -e 's/ + +/ + /g' | sed -e 's/ + x/ \* /g' | sed -e 's/ + \([^1]\)/\1/g' | sed -e 's/x/ \* /g'))) ($(($(echo "$*" | sed -e 's/ //g' | sed -e 's/|/1 p /g' | sed -e 's/ p $//g' | sed -e 's/x/2 p /g' | sed -e 's/+/2 p /g' | sed -e 's/p/+/g'))) tandenstokers)"

相关内容