Necesito tomar la suma de un campo delimitado por tilde (~). El problema que tengo es que a mis datos también se les ha escapado el delimitador.
Ejemplo
1~CEO~ashok\~kumar~1000
Como vemos en el tercer campo anterior, hemos escapado el delimitador que quiero evitar. Estoy ejecutando el siguiente comando que no maneja esto.
$ cat test.out|awk -F'~' 'BEGIN {sum=0} {sum+=$4} END{print sum}'
Suponga que los test.out
datos son:
1~CEO~ashok\~kumar~1000
2~CFO~Ranjan~2000
3~CEO~kumar~1000
Entonces mi salida debería ser 4000. ¡Pero actualmente con mi comando solo obtengo 3000!
Respuesta1
Simplemente cambie el delimitador de escape a otra cosa antes de procesar con awk
. Esto se puede hacer con sed
:
$ cat test.out| sed 's/\\~/=/g' | \
awk -F'~' 'BEGIN {sum=0} {sum+=$4} END{print sum}'
4000
Y, como suele ocurrir, cat
no es necesario:
$ sed 's/\\~/=/g' test.out | awk -F'~' 'BEGIN {sum=0} {sum+=$4} END{print sum}'
Respuesta2
Aquí hay una alternativa que no usa awk
:
$ sed 's/\\~/=/g' test.out | cut -d"~" -f4 | paste -sd+ | bc
4000
Lo anterior se utiliza sed
para cambiar la tilde escapada \~
en la tercera columna. Luego podemos utilizar cut
para seleccionar la cuarta columna de números y luego reconstruirlos para que estén separados por signos más, ( +
).
$ sed 's/\\~/=/g' test.out | cut -d"~" -f4 | paste -sd+
1000+2000+1000
Luego, esta cadena se entrega a la calculadora binaria, bc
que los resume.
Respuesta3
Para lidiar con el escape, un método general es usar perl
PCRE y su operador de expresión regular alternado combinado con el operador sin retroceso. Aquí con 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
lo que da:
1000
2000
1000
10000
(que puedes sumar con tu habitual awk '{s+=$0};END{print s}'
).
Con GNU sed
, también puedes hacerlo con:
sed -rn 's/((\\.|[^\~])*~){3}((\\.|[^~])*).*/\3/p'
Con GNU awk
, puede utilizar FPAT
para definir campos como secuencias de caracteres de escape o caracteres sin tilde ni barra invertida:
awk -v FPAT='(\\\\.|[^\\\\~])*' '{print $4}'
Respuesta4
Esto es un poco torpe en awk (a menos que pueda preprocesar su fuente para cambiar el delimitador, pero eso requiere conocer otro carácter o secuencia de caracteres que no puede aparecer en la entrada). Una cosa que puede hacer es leer una línea completa, luego masajear la línea para obtener líneas nuevas como separadores (las líneas nuevas son lo único que no puede aparecer en una línea).
awk 'BEGIN {FS="\n"}
{
gsub("~", "\n");
gsub("\\\n", "~");
gsub("\\\\", "\\");
$0 = $0;
print $4;
}'