meaning - awk unix
En AWK, ¿es posible especificar "rangos" de campos? (8)
En AWK, ¿es posible especificar "rangos" de campos?
Ejemplo. Dado un archivo "foo" separado por tabuladores con 100 campos por línea, quiero imprimir solo los campos 32 a 57 para cada línea, y guardar el resultado en un archivo "barra". Qué hago ahora:
awk ''BEGIN{OFS="/t"}{print $32, $33, $34, $35, $36, $37, $38, $39, $40, $41, $42, $43, $44, $45, $46, $47, $48, $49, $50, $51, $52, $53, $54, $55, $56, $57}'' foo > bar
El problema con esto es que es tedioso escribir y propenso a errores.
¿Hay alguna forma sintáctica que me permita decir lo mismo de una manera más concisa y menos propensa a errores (como "$ 32 .. $ 57")?
(Sé que OP solicitó "en AWK" pero ...)
Usar expansión de bash en la línea de comando para generar lista de argumentos;
$ cat awk.txt
1 2 3 4 5 6 7 8 9
a b c d e f g h i
$ awk "{print $(c="" ;for i in {3..7}; do c=$c/$$i, ; done ; c=${c%%,} ; echo $c ;)}" awk.txt
3 4 5 6 7
c d e f g
explicación;
c="" # var to hold args list
for i in {3..7} # the required variable range 3 - 7
do
# replace c''s value with concatenation of existing value, literal $, i value and a comma
c=$c/$$i,
done
c=${c%%,} # remove trailing/final comma
echo $c #return the list string
colocado en una sola línea usando puntos y comas, dentro de $()
para evaluar / expandir en su lugar.
Además de la answer awk
de @Jerry, hay otras alternativas:
Usando cut
(se asume el delimitador de pestañas por defecto):
cut -f32-58 foo >bar
Usando perl
:
perl -nle ''@a=split;print join "/t", @a[31..57]'' foo >bar
Llego tarde, pero esto es rápido hasta el punto, así que lo dejo aquí. En casos como este, normalmente elimino los campos que no necesito con gsub e print. Ejemplo rápido y sucio, ya que sabe que su archivo está delimitado por pestañas, puede eliminar los primeros 31 campos:
awk ''{gsub(/^(/w/t){31}/,"");print}''
ejemplo de eliminar 4 campos porque es flojo:
printf "a/tb/tc/td/te/tf/n" | awk ''{gsub(/^(/w/t){4}/,"");print}''
Salida:
e f
Esto es más corto de escribir, más fácil de recordar y usa menos ciclos de CPU que bucles horrendos.
No sé cómo hacer la selección de campo en awk. Sé cómo soltar los campos al final de la entrada (ver abajo), pero no al principio. A continuación, la manera más difícil de dejar los campos al principio.
Si conoce un carácter c
que no está incluido en su entrada, podría usar la siguiente secuencia de comandos awk:
BEGIN { s = 32; e = 57; c = "#"; }
{ NF = e # Drop the fields after e.
$s = c $s # Put a c in front of the s field.
sub(".*"c, "") # Drop the chars before c.
print # Print the edited line.
}
EDITAR :
Y pensé que siempre puedes encontrar un personaje que no está en la entrada: usa /n
.
Puede usar una combinación de bucles y printf
para eso en awk:
#!/bin/bash
start_field=32
end_field=58
awk -v start=$start_field -v end=$end_field ''BEGIN{OFS="/t"}
{for (i=start; i<=end; i++) {
printf "%s" $i;
if (i < end) {
printf "%s", OFS;
} else {
printf "/n";
}
}}''
Esto se ve un poco hacky, sin embargo:
- delimita adecuadamente su salida en función del
OFS
especificado, y - se asegura de imprimir una nueva línea al final para cada línea de entrada en el archivo.
Puedes hacerlo en awk usando intervalos RE. Por ejemplo, para imprimir los campos 3-6 de los registros en este archivo:
$ cat file
1 2 3 4 5 6 7 8 9
a b c d e f g h i
sería:
$ gawk ''BEGIN{f="([^ ]+ )"} {print gensub("("f"{2})("f"{4}).*","//3","")}'' file
3 4 5 6
c d e f
Estoy creando un segmento RE f para representar cada campo más su siguiente separador de campo (por conveniencia), luego estoy usando eso en gensub para eliminar 2 de esos (es decir, los primeros 2 campos), recuerde los siguientes 4 para referencia luego use / 3 y luego elimine lo que viene después de ellos. Para su archivo separado por tabuladores donde desea imprimir los campos 32-57 (es decir, los 26 campos después de los primeros 31), usted usaría:
gawk ''BEGIN{f="([^/t]+/t)"} {print gensub("("f"{31})("f"{26}).*","//3","")}'' file
Lo anterior usa GNU awk para su función gensub (). Con otras awks usarías sub () o match () y substr ().
EDITAR: Aquí se explica cómo escribir una función para hacer el trabajo:
gawk ''
function subflds(s,e, f) {
f="([^" FS "]+" FS ")"
return gensub( "(" f "{" s-1 "})(" f "{" e-s+1 "}).*","//3","")
}
{ print subflds(3,6) }
'' file
3 4 5 6
c d e f
Simplemente configure FS como corresponda. Tenga en cuenta que esto necesitará un ajuste para el FS predeterminado si su archivo de entrada puede comenzar con espacios y / o tener múltiples espacios entre campos y solo funcionará si su FS es un solo carácter.
Utilizo esta función simple, que no comprueba que el rango de campo exista en la línea.
function subby(f,l, s) {
s = $f
for(i=f+1;i<=l;i++)
s = sprintf("%s %s",s,$i)
return s
}
Versión levemente revisada:
BEGIN { s = 32; e = 57; }
{ for (i=s; i<=e; i++) printf("%s%s", $(i), i<e ? OFS : "/n"); }