problema de script de shell com o uso dos operadores

4

Estou comparando duas strings usando o algoritmo levenshtein distance . Eu tenho a implementação PHP do algoritmo de aqui . Eu estou chamando esta implementação php do meu script de shell como abaixo.

levenshtein_return=$(php levensh.php "String1" "String2")

A variável levenshtein_return contém o valor como 1 agora, pois há uma diferença de caractere único.

Agora, antes de inserir o valor como tal nas tabelas do banco de dados, preciso executar algumas operações aritméticas usando essa variável. Eu estou tentando implementar como,

table_value=$( expr( 1/ ( 1 + $levenshtein_return ) ) )

No entanto, se eu usar a sintaxe acima, estou recebendo um erro como

line 52: syntax error near unexpected token '1'

Como posso alterar a instrução expr para que eu possa obter o valor real da variável table_value ?

    
por Ramesh 18.02.2014 / 02:25

3 respostas

4

Você não deve usar expr neste caso, tente isto:

table_value=$(( 1 / (1+$levenshtein_return) ))
    
por 18.02.2014 / 02:38
1

Existem alguns problemas aí.

Em:

table_value=$( expr( 1/ ( 1 + $levenshtein_return ) ) )

Os caracteres ( e ) são especiais para o shell, portanto, deve ser escapado. E é o comando expr que você deseja executar, não o comando expr( . Além disso, você deve passar operadores e operandos como argumentos separados para expr .

table_value=$(expr '(' 1 / "(" 1 + "$levenshtein_return" \) ')' )

(acima, mostrando as diferentes maneiras de citar os caracteres ( e ) ).

Agora, como @Gnouc mostrou , expr não é mais necessário para avaliação aritmética como todos os POSIXs modernos shells têm um operador interno $((...)) para isso.

Agora, exceto com zsh e ksh93 , nem expr nem $((...)) fazem aritmética de ponto flutuante. Então, em ambos:

table_value=$(expr 1 / "(" 1 + "$levenshtein_return" ")" )

e

table_value=$((1 / (1 + $levenshtein_return)))

O valor será inteiro, então tipicamente 0 ou 1.

Se você quisesse ter 1 se as strings forem iguais e 0 se forem diferentes (mas, você não precisa calcular a distância se tudo o que você quer fazer é verificar se as duas strings são as mesmas) , então você teria escrito:

table_value=$((!$levenshtein_return))

Em ksh93 ou zsh , se você quisesse um valor de ponto flutuante, escreveria:

table_value=$((1. / (1 + $levenshtein_return)))

Em outros shells, e POSIXly, se você precisar de aritmética de ponto flutuante, normalmente chama um comando que pode fazer isso como awk , bc ou ... php .

Aqui, como você já está invocando php , é melhor fazer o cálculo final também. Se estiver usando awk , observe que awk também pode fazer o cálculo de distância do Levenshtein com facilidade:

table_value=$(awk '
    function min(x, y) {
      return x < y ? x : y
    }
    function lev(s,t) {
      m = length(s)
      n = length(t)

      for(i=0;i<=m;i++) d[i,0] = i
      for(j=0;j<=n;j++) d[0,j] = j

      for(i=1;i<=m;i++) {
        for(j=1;j<=n;j++) {
          c = substr(s,i,1) != substr(t,j,1)
          d[i,j] = min(d[i-1,j]+1,min(d[i,j-1]+1,d[i-1,j-1]+c))
        }
      }

      return d[m,n]
    }

    BEGIN {print 1 / (1 +lev(ARGV[1], ARGV[2])); exit}' String1 String2
)
    
por 18.02.2014 / 08:00
1

Embora não seja documentado e não seja garantido pelo POSIX, parece que, como o POSIX requer shells para manipular constantes C e muitos dos mesmos operadores C, eles parecem originar todo o resto dos operadores matemáticos integrais C simples, bem , incluindo testes e atribuições ternárias:

% a=abc
% b=abcd
% t=1 ; f=0
% echo $((r=${#a}>${#b}?t:f)) ; echo $r
> 0 
> 0
% echo $((r=${#a}<${#b}?t:f)) ; echo $r
> 1
> 1

Acabei de testar o exemplo básico acima em zsh, bash, sh do bash e dash. Todos se comportaram corretamente e identicamente.

    
por 12.03.2014 / 12:56