Script de shell para mover arquivos se determinada porcentagem de linhas contiver uma determinada string

3

Estou trabalhando em um shell script para mover arquivos se determinada porcentagem das linhas contiver uma determinada string.

Eu tenho ~ 2000 arquivos em um diretório que cada um contém uma única coluna de dados. O número de linhas em cada arquivo varia. A primeira linha é um cabeçalho e as outras linhas contêm números maiores que 0, com 6 casas decimais. Ex:

OMEGA
0.000010
0.000010
0.042214
0.042214
0.042214
0.042214
1.147412

Estou interessado em todos os arquivos em que pelo menos 10% das linhas contenham um valor maior que 1. Estou usando a proporção de linhas contendo "1". para "." - mas eu estou lutando com a sintaxe. ".omega" é a extensão dos arquivos nos quais estou interessado.

Aqui está o que eu tenho:

for file in *.omega
do
 if [ $(($(grep '1.' $file | wc -l) / $(grep '.' $file | wc -l)) * 100) -ge 10 ]; then
 mv $file positive_COGs/ 
 fi
done

Eu brinquei com o posicionamento de parênteses / colchetes e não obtive êxito. Além disso - não tenho certeza se é apropriado usar "-ge" para comparar o% (não um inteiro) com o inteiro de "10" (?).

Qualquer sugestão é muito apreciada. Estou usando o bash para executar o script.

Felicidades!

    
por A. Lindsey 02.05.2015 / 03:23

3 respostas

2

O problema é que o bash faz a aritmética do inteiro, então se você aceitar, digamos 20/50 que é sempre 0. Então, o teste que conta > 1 dividido por número de linhas é 0, então 0 * 100 é 0, que sempre será menor que 10.

Se você fosse multiplicar por 100 antes da divisão, eu acho que você conseguiria o que queria.

    
por 02.05.2015 / 03:31
1

Com uma versão recente 4.x do GNU awk você poderia fazer:

awk '
  BEGINFILE { count = 0 }
  FNR == 1  { next }
  $1 > 1.0  { count++ }
  ENDFILE   { if (count/(FNR-1) >= 0.1) printf "mv %s positive_COGs\n", FILENAME }
' *.omega | sh

Inicializa os contadores ( BEGINFILE ), ignora as linhas de cabeçalho ( FNR == 1 ), conta de acordo com os números encontrados nos arquivos de dados e imprime os arquivos (ou os comandos shell) correspondentes à condição (%código%). Os comandos ENDFILE são então alimentados em um shell para executar o movimento.

    
por 02.05.2015 / 11:57
0

/ na expressão aritmética executa uma divisão inteira, ou seja, é o operador de quociente. A maioria das shells só pode fazer aritmética inteira, não aritmética de ponto flutuante.

Em vez de dividir pelo total e depois multiplicar por 100, faça o oposto. Enquanto você está nisso, grep … | wc -l pode ser simplificado para grep -c .

Além disso, grep '1.' está errado: ele seleciona todas as linhas que contêm 1 seguido por outro caractere, ou seja, linhas que contêm 1 em algum outro lugar que não seja o final da linha. Para selecionar linhas com um número entre 10 k + 1 e 10 k + 2, use grep '1\.' . Para selecionar linhas que contenham um número maior que 1, use grep '[1-9][0-9]*\.' .

Observe que grep . seleciona linhas não vazias. Para selecionar todas as linhas, use wc -l . Para selecionar apenas linhas que tenham um número, use algo como grep '[0-9]' (isso inclui linhas com um dígito em qualquer lugar, não apenas uma linha contendo apenas um número).

if [ $(($(grep -c '[1-9][0-9]\.' <"$file") * 100 / $(grep -c '[0-9]' <"$file"))) -ge 10 ]; then

Seria potencialmente mais rápido e mais robusto processar o arquivo apenas uma vez e contar os números conforme eles chegam. Você pode fazer isso com o awk.

if awk '
    $0 >= 1 {good += 1}
    $0 != 0 || $0 ~ /^ *0*\.0*$/ {total += 1}
    END {if (good < total/10) exit(1)}
'; then
    
por 02.05.2015 / 23:19