operações aritméticas dentro da coluna com awk ou sed

3

Eu tenho um arquivo cujas colunas contêm equações aritméticas simples que eu gostaria de mesclar ao resultado aritmético.

Amostra de entrada (colunas separadas por tabulações):

+104-1+12   6   +3

Eu gostaria de calcular a soma aritmética dentro de cada coluna. Se uma coluna não contém nenhum sinal aritmético, eu a trato porque ela contém um + antes do item. Embora seja fácil usar sed para adicionar um sinal + se uma coluna começar sem sinal ( sed -E 's/(\t)([0-9]*)/\t+/g' funcionaria, supondo que uma linha nunca comece com um dígito, como no exemplo)

A saída que eu esperaria é a seguinte:

115 6   3

Como posso conseguir isso no unix? As soluções awk / sed são preferidas.

    
por dovah 09.05.2017 / 14:13

5 respostas

6

Você pode usar perl :

perl -pe 's/[\d+-]+/eval$&/ge' your-file

Ou até mesmo:

perl -pe 's/[\d+-]+/$&/gee' your-file (thanks Rakesh)

Mesmo com zsh :

set -o extendedglob # for the ## operator (same as ERE +)
while IFS= read -r line; do 
  printf '%s\n' ${line//(#m)[0-9+-]##/$((MATCH))}
done < your-file

Ou:

zmodload zsh/mapfile
set -o extendedglob
printf %s ${mapfile[your-file]//(#m)[0-9+-]##/$((MATCH))}

Em todos os quatro, procuramos sequências de dígitos, - e + caracteres e passamos para o processador aritmético do intérprete ( eval in perl (ou a ee flag que causa a expansão da substituição a ser avaliada como perl code), $((...)) in zsh ).

Não estamos validando as expressões antes de transmiti-las ao interpretador, por isso pode causar falhas (por exemplo, em sequências como -+- ou 3++ ), mas pelo menos porque estamos considerando apenas dígitos e - / + caracteres, não deve fazer muito mais mal do que reportar uma mensagem de erro e abortar o comando.

    
por 09.05.2017 / 14:29
3

Eu não vou duplicar a resposta Adição com 'sed' ; nem encontrei um jeito no awk, mas aqui está uma versão bash:

while IFS= read -r line
do
  set -f; set -- $line
  for e in "$@"
  do
    printf "%d " "$(( e ))"
  done
  echo
done < input
    
por 09.05.2017 / 14:51
2
sed -E 's/(\t)([0-9])/+/g' data.file |
while IFS= read -r l; do
   set -f; IFS=$'\t'
   printf '0%s\n' $l | bc -l | paste -s -
done
sed -e 's/\t\([0-9]\)/\t+/' data.file |
while IFS= read -r l; do
   set -f; IFS=$'\t'
   printf '0%s\n' $l | bc -c |
   sed -ne '
      $!{
         y/:@irKW/      /
         s/[^ 0-9]/ & /g
         s/[ ][ ]*/ /g;s/^[ ]*//;s/[ ]*$/p/p
      }
   ' | dc | paste -s -
done

Aqui geramos uma representação postfix da expressão matemática e antes de passá-la para a calculadora postfix dc , limpamos a informação não matemática da saída do comando bc -c .

Resultado

115     6       3
    
por 09.05.2017 / 14:34
1

Usando awk getline de um canal

awk '{
  for (i=1;i<=NF;i++) {
    sub(/^\+/,"",$i); 
    cmd = sprintf("echo %s | bc -l", $i); 
    cmd | getline $i; close(cmd);
  }
} 1' file
115 6 3
25 6 2 69 57
    
por 09.05.2017 / 15:06
0

Aqui está uma solução totalmente "awk" aproveitando a capacidade do awk para organizar representações de números de string em representações numéricas, sem uso de executáveis externos:

awk -F"\t" \
'BEGIN { OFS="\t" }
 { gsub(/-/,"|-") 
   gsub(/\+/,"|")
   for(i=1; i<=NF; i++) { ## iterate over columns
     num_parts=split($i,parts,"|")
     for(j=1; j<=num_parts; j++) ## iterate over arithmetic expression parts
       sums[i] += parts[j]+0 ## Adding zero marshals the string into a numeric
   }}
 END{
      for(i=1; i<=NF; i++) { 
        if(i>1) printf OFS
        printf sums[i]
      } 
      print "" }' file
    
por 26.05.2017 / 08:25