Preciso pegar a soma de um campo delimitado por til (~). O problema que tenho é que meus dados também estão tendo escape do delimitador.
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 resolve isso.
$ cat test.out|awk -F'~' 'BEGIN {sum=0} {sum+=$4} END{print sum}'
Suponha os test.out
dados como:
1~CEO~ashok\~kumar~1000
2~CFO~Ranjan~2000
3~CEO~kumar~1000
Portanto, minha saída deveria ser 4.000. Mas atualmente com meu comando recebo apenas 3.000!
Responder1
Basta alterar o delimitador de escape para outra coisa 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 costuma acontecer, cat
não é necessário:
$ sed 's/\\~/=/g' test.out | awk -F'~' 'BEGIN {sum=0} {sum+=$4} END{print sum}'
Responder2
Aqui está uma alternativa que não usa awk
:
$ sed 's/\\~/=/g' test.out | cut -d"~" -f4 | paste -sd+ | bc
4000
O procedimento acima é usado sed
para trocar o til de escape \~
na 3ª coluna. Depois podemos cut
selecionar a 4ª 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
Essa string é então fornecida à calculadora binária, bc
que os resume.
Responder3
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 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 habitual awk '{s+=$0};END{print s}'
).
Com GNU sed
, você também pode fazer isso com:
sed -rn 's/((\\.|[^\~])*~){3}((\\.|[^~])*).*/\3/p'
Com GNU awk
, você pode usar FPAT
para definir campos como sequências de caracteres de escape ou caracteres que não sejam til nem barra invertida:
awk -v FPAT='(\\\\.|[^\\\\~])*' '{print $4}'
Responder4
Isso é um pouco desajeitado no awk (a menos que você possa pré-processar sua fonte para alterar o delimitador, mas isso requer conhecer outro caractere ou sequência de caracteres que não pode 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 não pode aparecer em uma linha).
awk 'BEGIN {FS="\n"}
{
gsub("~", "\n");
gsub("\\\n", "~");
gsub("\\\\", "\\");
$0 = $0;
print $4;
}'