Tengo una gran cantidad de archivos en una carpeta en particular. Me gustaría mover estos archivos a una subcarpeta SÓLO SI tienen al menos 1 valor superior a 0,5 en cualquier fila de la columna 4. En un comando separado me gustaría hacer lo mismo pero con archivos que tengan al menos 2 filas con valores superiores 0,5 en la columna 4.
Este es el formato general de los archivos (con encabezado):
col1 col2 col3 col4 col5 col6
ABC DEF 5.10 0.94 GHI JKL
MNO PQR 8.31 0.37 STU VWX
ABC DEF 6.49 0.84 GHI JKL
MNO PQR 3.32 0.21 STU VWX
Algunos de los números de la columna 4 están en notación científica: 8.934553871039306e-05
El siguiente código es lo que he intentado hasta ahora para mover archivos con al menos 1 valor superior a 0,5 en la columna 4. Termina moviendo todos los archivos a la subcarpeta, incluso aquellos que no coinciden con la condición.
#!/bin/bash
find . -type f -exec awk '$4 >= 0.5' {} \; -exec mv -n {} ./NewFolder/ \;
Respuesta1
Para que su comando funcione, debe awk
salir con el código 0 si se encuentra una coincidencia o con un código de salida distinto de cero si no se encuentra ninguna coincidencia.
Además de esto, debes omitir la primera línea porque un valor no numérico se comparará como una cadena, lo que puede generar una coincidencia inesperada.
find . -type f -exec awk 'FNR==1 {next} $4 >= 0.5 {found=1; exit} END {exit !found}' {} \; -exec mv -n {} ./NewFolder/ \;
Nota: Si awk
se llama al script con más de un archivo, el código de salida significa que se encontró una coincidencia en cualquiera de los archivos. El find
comando se asegurará de que solo se pase un archivo a la vez awk
, por lo que esto no es un problema aquí.
2da edición:
Para seleccionar archivos que tengan al menos 2 filas coincidentes, puede contar las coincidencias.
find . -type f -exec awk 'FNR==1 {next} $4 >= 0.5 {found++; if(found >= 2) exit} END {exit found >= 2}' {} \; -exec mv -n {} ./NewFolder/ \;
Editar:
Para depurar el problema de que el script mueve archivos que no tienen un valor coincidente en la columna 4, puede agregar código al awk
script para imprimir información sobre la línea coincidente. El siguiente código imprimirá el nombre del archivo, el número de línea y la línea coincidente si se encuentra una coincidencia.
find . -type f -exec awk 'FNR==1 {next} $4 >= 0.5 {found=1; printf "%s:%d:%s\n", FILENAME, FNR, $0; exit} END {exit !found}' {} \; -exec mv -n {} ./NewFolder/ \;
Obtendrás algo como
threshold.txt:2:ABC DEF 5.10 0.94 GHI JKL
Sugiero hacer esto primero para encontrar la causa del problema.
Si hay líneas que tienen un texto no numérico en la columna 4, los valores se compararán como texto. Esto daría como resultado, por ejemplo, "abc"
un valor mayor que "0.5"
.
Otra posible causa podría ser una línea que tenga espacios en la columna 1 o 2, lo que provocará una asignación incorrecta del texto a las columnas.
En caso de que haya valores no numéricos en la columna 4 y desee ignorar estas líneas, puede forzar una interpretación numérica agregando el valor como 0
en 0 + $4
.
find . -type f -exec awk 'FNR==1 {next} 0 + $4 >= 0.5 {found=1; printf "%s:%d:%s\n", FILENAME, FNR, $0; exit} END {exit !found}' {} \; -exec mv -n {} ./NewFolder/ \;
Si el motivo del problema es que sus campos están separados por una tabulación y los valores pueden contener espacios, puede especificar el separador de campo ( -F "\t"
). El siguiente script combina esto con las otras modificaciones.
find . -type f -exec awk -F "\t" 'FNR==1 {next} 0 + $4 >= 0.5 {found=1; printf "%s:%d:%s\n", FILENAME, FNR, $0; exit} END {exit !found}' {} \; -exec mv -n {} ./NewFolder/ \;
Respuesta2
En awk
realidad, no funciona, encontrará todos los archivos porque la cadena col4
satisface >=0.5
:
$ echo col4 | awk '$1>=0.5'
col4
Entonces debes saltarte el encabezado. También debe decirle a awk que salga con éxito si el archivo coincide con sus criterios y con error si no es así. Algo como esto:
find . -type f \
-exec awk -va=1 '{ if($4 >= 0.5 && NR>1){a=0}} END{exit a}' {} \; \
-exec mv -n {} ./NewFolder/ \;
Respuesta3
Con un bucle for puedes probar esto:
for i in *; do # *.extension
[[ -f "$i" && $(awk 'NR>1 && $4 >= 0.5' "$i") ]] && mv "$i" NewFolder/
done
Y para dos valores:
for i in *; do # *.extension
[[ -f "$i" ]] && [[ $(awk 'NR>1 && $4 >= 0.5' "$i" | wc -l) -ge 2 ]]
mv "$i" NewFolder
done