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

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

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.outdados 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, catnã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 sedpara trocar o til de escape \~na 3ª coluna. Depois podemos cutselecionar 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, bcque os resume.

Responder3

Para lidar com o escape, um método geral é usar perlou 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 FPATpara 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;
}'

informação relacionada