Adição com 'sed'

36

Estou tentando executar uma operação matemática com sed , mas ela continua a tratar minhas variáveis como strings. A entrada é deste tipo:

$ echo 12 | sed 's/[0-9]*/&+3/'
$ 12+3

Eu gostaria de ter 15 como saída. Eu preciso fazer a operação e substituir seu resultado matemático em apenas uma passagem, porque eu estou rodando o programa como um daemon do Python, e eu quero evitar passagens como redirecionar stdout em arquivos, abrir esses arquivos, executar operações, extrair o resultado, faça a substituição. Para mim, sed parece ser o melhor para realizar tudo em uma linha.

Eu tentei converter as entradas e saídas de várias maneiras, como

$ echo 12 | sed 's/[0-9]*/int(&+3)/'
$ echo 12 | sed 's/[0-9]*/\int(&+3)/'
$ echo 12 | sed 's/[0-9]*/\int(&+3)/'

mas o resultado foi sempre uma impressão do segundo campo.

    
por Luigi Tiburzi 20.04.2012 / 12:50

9 respostas

79

Se você honestamente quiser usar sed, esse é o caminho a seguir:

s/[0-9]/<&/g
s/0//g; s/1/|/g; s/2/||/g; s/3/|||/g; s/4/||||/g; s/5/|||||/g; s/6/||||||/g
s/7/|||||||/g; s/8/||||||||/g; s/9/|||||||||/g
: tens
s/|</<||||||||||/g
t tens
s/<//g
s/+//g
: minus
s/|-|/-/g
t minus
s/-$//
: back
s/||||||||||/</g
s/<\([0-9]*\)$/<0/
s/|||||||||/9/; s/||||||||/8/; s/|||||||/7/; s/||||||/6/; s/|||||/5/; s/||||/4/
s/|||/3/; s/||/2/; s/|/1/
s/</|/g
t back

Entrada:

1+2
100+250
100-250

Saída:

3
350
-150

Sua missão, se você escolher aceitá-la, é implementar multiplicação.

    
por 20.04.2012 / 15:03
18

sed não é a melhor opção aqui, ele não faz aritmética nativamente (veja Incrementar um número para como você poderia fazê-lo embora). Você poderia fazer isso com awk :

$ echo 12 | awk '{print $0+3}'
15

O melhor código a ser usado dependerá do formato exato de sua entrada e do que você deseja / precisa fazer se não for numérico ou contiver mais de um número, etc.

Você também pode fazer isso apenas com bash :

$ echo $(( $(echo 12) + 3 ))

ou usando expr de maneira semelhante.

    
por 20.04.2012 / 13:11
17

Tentei aceitar o seu desafio @Richter, foi o que fiz usando parte do seu código:

sed 's/[0-9]/<&/g
s/0//g; s/1/|/g; s/2/||/g; s/3/|||/g; s/4/||||/g; s/5/|||||/g; s/6/||||||/g
s/7/|||||||/g; s/8/||||||||/g; s/9/|||||||||/g
: tens
s/|</<||||||||||/g
t tens
s/<//g
s/.*\*$/0/
s/^\*.*/0/
s/*|/*/
: mult
s/\(|*\)\*|/<*/ 
t mult
s/*//g
s/<//g
: back
s/||||||||||/</g
s/<\([0-9]*\)$/<0/
s/|||||||||/9/; s/||||||||/8/; s/|||||||/7/; s/||||||/6/; s/|||||/5/; s/||||/4/
s/|||/3/; s/||/2/; s/|/1/
s/</|/g
t back'

Entrada:

04*3
4*3
40*3
42*32
150*20
1*3
3*1
0*3
3*0

Saída: todos os resultados corretos

    
por 24.04.2012 / 16:51
10

perl permite uma construção muito semelhante a sed 's ... uma diferença é que perl pode fazer coisas mais complexas ... sed é muito bom para subestrições de texto simples

 echo 'a12' | perl -pe 's/([0-9]+)/($1+3)/e'  # the trailing /e means evaluate

saída

a15
    
por 20.04.2012 / 13:43
8

apenas alimente a corda em uma calculadora

 echo 12 | sed 's/[0-9]*/&+3/' | bc
    
por 20.04.2012 / 13:43
5

Se você definitivamente tiver que combinar expressões regulares e operações aritméticas, escolha um idioma em que o parâmetro de substituição da expressão regular possa ser uma função de retorno de chamada.

Perl, Ruby, JavaScript e Python são essas linguagens:

bash-4.2$ echo 12 | perl -pe 's/\d+/$&+3/e'
15

bash-4.2$ echo 12 | ruby -pe '$_.sub!(/\d+/){|s|s.to_i+3}'
15

bash-4.2$ echo 12 | js -e 'print(readline().replace(/\d+/,function(s){return parseInt(s)+3}))'
15

bash-4.2$ echo 12 | python -c 'import re;print re.sub("\d+",lambda s:str(int(s.group(0))+3),raw_input())'
15
    
por 20.04.2012 / 13:41
5

Eu realmente não entendo porque a extrema complexidade da resposta aceita, qualquer um dos abaixo, faça o que você quer:

echo 12 | sed 's/[0-9]*/echo \$(( & + 3 ))/e'

ou

echo 12 | sed 's/[0-9]*/expr & + 3/e'

Eu acho que pode exigir o GNU sed, mas não tenho certeza.

    
por 30.03.2013 / 13:56
1

Outra solução simples de bash , que realmente funciona em um pipe:

 echo 12 | { read num; echo $(( num + 3)); }
    
por 20.04.2012 / 13:37
1

Se você misturar um pouco de bashismo:

echo $(($(echo 12 | sed 's/[0-9]*/&+3/')))

Para extrair o número de um texto:

echo $(($(echo "foo12bar" | sed -r 's/[^0-9]*([0-9]*).*/+3/')))

Sem sed, apenas bash:

var="foo12bar"
echo $((${var//[^0-9]/}+3))

substitui todos os dígitos não ${var//[^0-9]/} e faz aritmética em parênteses redondos duplos: $((x+3))

    
por 20.04.2012 / 17:08

Tags