
Tengo una gran cantidad de archivos en los que necesito eliminar filas específicas y luego colocar los datos que saqué en una hoja de cálculo. Un ejemplo sería mi archivo muestra:
Name: w
Age: x
Height: y
Weight: z
Solo quiero la edad, la altura y el peso, así que primero ejecuté:
grep -E 'Age|Height|Weight' [input file] > output.txt
Debido a la cantidad de archivos, mi salida ahora se ve así
Age 1
Height 1
Weight 1
Age 2
Height 2
Weight 2
etc...
Lo que ahora quiero es ejecutar un script awk para que revise mi nuevo archivo output.txt y primero encuentre cada fila con la palabra 'Edad' y la imprima. Una vez que ha hecho todos los de 'Edad', hace la altura y luego el peso. Ejecuté el script:
awk -F"\t" '/Age/ {print} /Height/ {print}' output.txt >output2.txt
Pero si solo lo imprime como el archivo original. ¿Cómo lo cambio para que después de haber hecho todos los de Edad, encuentre los de altura?
EDITAR:
Mi resultado deseado es que el archivo sea
Edad 1
Edad 2
Altura 1
Altura 2
Peso 1
Peso 2
etc..
Solo para aclarar, Edad 1 es la fila con "edad" del archivo 1, etc.
Respuesta1
awk solo ejecuta el archivo una vez de forma predeterminada, ejecutando todos los bloques en orden, razón por la cual le brinda el resultado que obtuvo. Puedes obtener el comportamiento que deseas usandouna matrizpara guardar las líneas a medida que avanza, sin dejar de procesar el archivo solo una vez:
BEGIN {
AgeIndex = 1
HeightIndex = 1
}
/Age/ {
ages[AgeIndex] = $0
AgeIndex+=1
}
/Height/ {
heights[HeightIndex] = $0
HeightIndex+=1
}
END {
for (x = 1; x < AgeIndex; x++)
print ages[x] "\n"
for (x = 1; x < HeightIndex; x++)
print heights[x] "\n"
}
Guárdelo en, digamos, filter.awk
y luego ejecute:
awk -f filter.awk output.txt > output2.txt
para obtener el resultado que desea:
$ awk -f filter.awk < data
Age 1
Age 2
Height 1
Height 2
Lo que estamos haciendo es crear dos matrices ages
y heights
guardar cada línea coincidente en ellas a medida que avanzamos. AgeIndex
mantiene hasta qué punto estamos en la matriz. Al final, imprimimos cada línea que guardamos (y una nueva línea adicional como la que desee), primero todas las edades, luego todas las alturas.
Las matrices mantendrán el archivo completo en la memoria al final, por lo que si su archivo es particularmente grande, tendrá que compensar el uso de memoria por el tiempo para revisar el archivo completo más de una vez. En este punto, es esencialmente lo mismo que un programa en cualquier otro idioma: si no tiene ningún motivo particular para usar awk, es posible que prefiera otro idioma. Para ser honesto, creo que lo recomendaría: awk no te ofrece mucho aquí.
Respuesta2
Con gawk
:
$ awk -F"\t" '
{ a[$1]++ }
END {
n = asorti(a,b);
for (i = 1; i <= n; i++) {
print b[i];
if (i%2 == 0) {
printf "\n";
}
}
}
' output.txt
Age 1
Age 2
Height 1
Height 2
Weight 1
Weight 2
Respuesta3
Supongo que las líneas en blanco no son parte de su archivo real, o que al menos no le importan. Si es así, todo lo que necesitas es sort
:
$ cat output.txt
Age 1
Height 1
Weight 1
Age 2
Height 2
Weight 2
$ sort output.txt
Age 1
Age 2
Height 1
Height 2
Weight 1
Weight 2
Sin embargo, a menos que sus archivos sean demasiado grandes para guardarlos en la memoria, podría ser más sencillo hacer todo en un solo paso:
grep -whE 'Age|Height|Weight' *txt | sort > outfile
Lo anterior buscará Age
o Height
o Weight
en todos los archivos cuyo nombre termine txt
en el directorio actual ( *txt
). Significa -w
"coincidir solo con palabras completas" (para que Age
no coincidan, Ageing
por ejemplo), es -h
necesario porque sin él, el nombre del archivo se imprime junto con la línea coincidente cuando se proporciona más de un archivo de entrada. Permite -E
expresiones regulares extendidas que nos dan |
OR.
NOTA: Si, por alguna razón, realmente desea una línea en blanco adicional entre cada entrada (que no es lo que grep
produciría su comando), puede agregarla con:
grep -whE 'Age|Height|Weight' *txt | sort | sed 's/$/\n/'
Ejemplo
$ for i in {1..3}; do echo -e "Name $i\nAge $i\nHeight $i\nWeight $i" > $i.txt; done
$ for f in *txt; do echo " -- $f --"; cat $f; done
-- 1.txt --
Name 1
Age 1
Height 1
Weight 1
-- 2.txt --
Name 2
Age 2
Height 2
Weight 2
-- 3.txt --
Name 3
Age 3
Height 3
Weight 3
$ grep -whE 'Age|Height|Weight' *txt | sort
Age 1
Age 2
Age 3
Height 1
Height 2
Height 3
Weight 1
Weight 2
Weight 3
En cualquier caso, incluso si sort
no es suficiente para usted, haría este tipo de cosas en Perl, no awk
(esto supone que desea líneas en blanco adicionales que, nuevamente, probablemente no desee):
$ perl -ane '$k{$F[0]}.=$_."\n" if /./;
END{print $k{$_},"\n" for sort keys (%k)}' output.txt
Age 1
Age 2
Height 1
Height 2
Weight 1
Weight 2
Puedes pasar eso head -n -2
para deshacerte de las dos últimas líneas en blanco si no las quieres.
Respuesta4
python
solución para este problema:
from collections import defaultdict
f = open("output.txt", "r")
d = defaultdict(list)
for line in f:
line = line.strip()
if line != '':
arr = line.split(" ")
d[arr[0]].append(arr[1])
print d.items()
Hice un hash usando la primera columna y la puse en una lista.