script bash com matemática simples envolvida

3

Eu sou novo em criação de scripts e quero escrever um script que precise processar seu (s) argumento (s) da seguinte maneira:

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

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

Onde eu recebo isso como saída:

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

O que ele faz é basicamente contar a quantidade de 'paus' e aplicar matemática simples a ele. A saída resultante é: 1. O (s) argumento (s) dado (s) com apenas espaços deixados antes e depois de um x ou + 2. 9 no exemplo acima é o resultado da matemática. 3. 11 é a quantidade de paus usados na fórmula (a + e a x contam como dois paus)

Até agora eu tenho o seguinte código e não entendo onde está o meu erro:

#!/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)"

Resolvemos esse problema começando do zero com uma abordagem diferente, o que resultou no seguinte código correto:

#!/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+]*\)/()/g' | sed -e 's/|/1 + /g' | sed -e 's/ + $//g' | sed -e 's/ + +/ + /g' | sed -e 's/ + x/ \* /g' | sed -e 's/ + \([^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)"
    
por noone 06.12.2016 / 22:17

3 respostas

1

Antes de tudo, para responder sua pergunta, -eq é para comparação de números inteiros. Use = para strings. Eu assumo que este é seu erro principal.

Alguns conselhos:

  • cite suas variáveis por padrão (por exemplo, "$n" , não apenas $n ). A não marcação pode ser necessária em alguns casos, mas deve ser uma exceção
  • use sempre set -u para detectar o uso de variáveis não inicializadas
  • concatene suas instruções sed em um comando sed: sed -e 's/ //g; s/[+x]/ & /g;' em vez do pipeline de três comandos sed -e 's/ //g' | sed -e 's/+/ + /g' | sed -e 's/x/ x /g'
  • think shell (Eu sei que isso é difícil quando você está aprendendo sh): divida seu código em pequenos comandos (funções), use parâmetros e aproveite o analisador de shell ( for word in "$@" ). Aqui, o que eu vejo é um grande loop com muito N++ e você está analisando tudo sozinho, caractere por caractere.

Para a diversão do code golf, aqui está a parte do cálculo em uma avaliação sed + aritmética:

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

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

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

Esta é uma solução radicalmente diferente. Tome isso como uma curiosidade, e não deixe cair o seu! O que eu faço é apenas transformar sua fórmula tandenstokers em uma fórmula aritmética regular, que eu finalmente avalio com $(( ... )) .

Deixo como um exercício a contagem de palitos de dente na fórmula.

    
por xhienne 06.12.2016 / 23:14
4

Eu tenho medo que seu roteiro não seja muito fácil de entender. Sua abordagem parece excessivamente complicada para o que ela está tentando fazer e, como você não está explicando o que você acha que cada parte do seu roteiro faz, não é trivial descobrir isso.

Dito isto, seu primeiro problema (que seria óbvio se você incluiu as mensagens de erro) é que você está usando -eq para comparação lexical. Você precisa de = , já que você está combinando strings, não números.

A próxima edição, que novamente seria óbvia das mensagens de erro, é

/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

Isso ocorre porque os vários ${uitdrukking:$x:$y} expandem para strings contendo espaços, não para um único caractere. Provavelmente, isso está acontecendo porque você está incrementando suas várias variáveis de contador em muitos lugares, portanto, seu $y será >1 muito rapidamente. Eu acho (mas, mais uma vez, posso ter certeza de que sua pergunta não explica realmente o que você acha que está acontecendo) que você entendeu mal como a sintaxe ${var:x:y} funciona. Não extrai a substring de var da posição x para a posição y . Ele extrai a substring de var a partir da posição x e y caracteres por muito tempo .

A primeira regra para programar em qualquer idioma é: quando algo dá errado, imprima todas as variáveis . 9 vezes em 10, o problema é que uma variável não tem o valor que você acha que tem.

Em qualquer caso, você tem muitos problemas para depurar aqui e seria muito mais simples exigir do zero. Por exemplo, esse script faz o que você deseja:

#!/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)"

Para ver em ação:

$ foo.sh '| | x | | | | + |'
|| x |||| + | = 9 (11 tandenstokers)
$ foo.sh \| \| x\|\| \| \|\+\|
|| x |||| + | = 9 (11 tandenstokers)
    
por terdon 06.12.2016 / 23:25
0

Esta é a minha solução de trabalho para o problema:

#!/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+]*\)/()/g' | sed -e 's/|/1 + /g' | sed -e 's/ + $//g' | sed -e 's/ + +/ + /g' | sed -e 's/ + x/ \* /g' | sed -e 's/ + \([^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)"
    
por noone 07.12.2016 / 19:20

Tags