¿Cómo puedo evitar caracteres delimitadores de escape en un comando de Unix?

¿Cómo puedo evitar caracteres delimitadores de escape en un comando de Unix?

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.outdatos 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, catno 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 sedpara cambiar la tilde escapada \~en la tercera columna. Luego podemos utilizar cutpara 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, bcque los resume.

Respuesta3

Para lidiar con el escape, un método general es usar perlPCRE 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 FPATpara 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;
}'

información relacionada