Como posso evitar caracteres delimitadores com escape em um comando Unix?

6

Eu preciso pegar a soma de um campo que é delimitado por til (~). O problema que tenho é que meus dados também estão tendo o delimitador escapado.

Exemplo

1~CEO~ashok\~kumar~1000

Como vemos no terceiro campo acima, escapamos do delimitador que desejo evitar. Estou executando o comando abaixo, que não lida com isso.

$ cat test.out|awk -F'~' 'BEGIN {sum=0} {sum+=$4} END{print sum}'

Assumir os dados test.out como:

1~CEO~ashok\~kumar~1000
2~CFO~Ranjan~2000
3~CEO~kumar~1000

Então minha saída deve ser 4000. Mas atualmente com o meu comando eu recebo apenas 3000!

    
por Ranjan 09.05.2014 / 06:57

6 respostas

1

Apenas altere o delimitador de escape para algo diferente antes de processar com awk . Isso pode ser feito com sed :

$ cat test.out| sed 's/\~/=/g' | \
    awk -F'~' 'BEGIN {sum=0} {sum+=$4} END{print sum}'
4000

E, como é frequentemente o caso, o cat não é necessário:

$ sed 's/\~/=/g' test.out | awk -F'~' 'BEGIN {sum=0} {sum+=$4} END{print sum}'
    
por 09.05.2014 / 07:06
1

Aqui está uma alternativa que não usa awk :

$ sed 's/\~/=/g' test.out | cut -d"~" -f4 | paste -sd+ | bc
4000

O acima usa sed para trocar o til% de escape\~ na terceira coluna. Depois, podemos usar cut para selecionar a quarta coluna de números e depois reconstruí-los para que fiquem separados por sinais de mais ( + ).

$ sed 's/\~/=/g' test.out | cut -d"~" -f4 | paste -sd+
1000+2000+1000

Esta string é então dada à calculadora binária, bc , que a resume.

    
por 09.05.2014 / 07:37
1

Para lidar com o escape, um método geral é usar perl ou PCRE e seu operador regexp de alternância combinado com o operador no-backtrack. Aqui com o GNU grep :

grep -Po '(?>(?:\.|.)*?~){3}\K(?:\.|[^~])*' << \EOF
1~CEO~ashok\~kumar~1000
2~CFO~Ranjan~2000
3~CEO~kumar~1000
4~field2~field3\~10000~field5-note-the-escaped-backslash-not-tilde
5~a\~b\~c\~no-4th-field-here
EOF

que dá:

1000
2000
1000
10000

(que você pode somar com o seu usual awk '{s+=$0};END{print s}' ).

Com o GNU sed , você também pode fazer isso com:

sed -rn 's/((\.|[^\~])*~){3}((\.|[^~])*).*//p'

Com o GNU awk , você pode usar FPAT para definir campos como sequências de caracteres com escape ou não-til-e-barra invertida:

awk -v FPAT='(\\.|[^\\~])*' '{print $4}'
    
por 14.05.2014 / 17:03
1

Parece que você deseja somar todo o valor na última coluna de cada linha. então tente isto:

$ awk -F'~' '{sum += $NF};END{print sum}' file
4000

Se você puder usar perl :

$ perl -F'~' -anle '$sum += $F[-1]; END {print $sum}' file
4000

ou:

$ perl -nle '$sum += $1 if /(\d+$)/; END {print $sum}' file
4000
    
por 10.05.2014 / 08:00
0

Isso é um pouco desajeitado no awk (a menos que você possa pré-processar sua origem para alterar o delimitador, mas isso requer conhecer outro caractere ou seqüência de caracteres que não podem aparecer na entrada). Uma coisa que você pode fazer é ler uma linha inteira e, em seguida, massagear a linha para obter novas linhas como separadores (novas linhas são a única coisa que possivelmente não pode aparecer em uma linha).

awk 'BEGIN {FS="\n"}
{
    gsub("~", "\n");
    gsub("\\n", "~");
    gsub("\\", "\");
    $0 = $0;
    print $4;
}'
    
por 10.05.2014 / 14:15
0
for n in $(cat <<\DATA
    1~CEO~ashok\~kumar~1000
    2~CFO~Ranjan~2000
    3~CEO~kumar~1000
DATA
)
    do r=$((r+${n##*~})) ; done
echo $r

###OUTPUT
4000

Dados os dados que você mostra, um simples for loop pode fazer isso, mas se houver uma chance de <tab> ou <space> nos dados, você pode querer limitar a divisão como:

IFS='
'

... primeiro.

Ou com sed e bc :

sed 's/.*[^0-9]/r+=/;$aprint r' <<\DATA | bc
    1~CEO~ashok\~kumar~1000
    2~CFO~Ranjan~2000
    3~CEO~kumar~1000
DATA

OUTPUT:

4000

Ambos os métodos dependem de seus dados de destino serem o último campo em cada linha.

O primeiro faz com que o shell simples seja iterável $(( aritmética )) em $((r)) depois de remover tudo, incluindo seu último delimitador de $n .

O segundo substitui tudo o que foi removido no primeiro método pela string 'r+=' e |pipes o resultado para bc , o que faz r=r+${last_field} . Na $ última linha de entrada sed também a ppends outra linha:

print r

... e |pipes para bc , que faz como instruído e gera como mostrado.

    
por 07.06.2014 / 02:57