Localiza arquivos para os quais existem várias variações nesse nome de arquivo juntas no mesmo diretório

5

Eu quero listar os arquivos para os quais existe, em um determinado diretório, TODOS estes arquivos:

  • <filename>.wed
  • <filename>.tis
  • <filename>.are
  • <filename>LM.bmp

Atualmente, estou fazendo isso com find e sed . Funciona, mas é deselegante e lento!

find . -iname "*.wed" -exec echo {} \; | sed s/.wed$// $1 | sed s/..// $1 | while read in; do find . -name "$in.are"; done | sed s/.are$// $1 | sed s/..// $1 | while read in; do find . -name "$in.tis"; done | sed s/.tis$// $1 | sed s/..// $1 | while read in; do find . -name "$in*.bmp"; done

Basicamente eu encadeio um find , dois sed e um while read para cada extensão que eu quero filtrar.

Leva > 35s por apenas 30 mil arquivos! Como posso melhorar isso?

Exemplo

Se no diretório houver arquivos chamados AR0505.are , AR0505.tis , AR0505.wed e AR0505LM.bmp , o script imprimirá "AR0505".

Se um ou mais desses arquivos estivessem faltando, o script não seria impresso.

    
por MasterScrat 21.03.2017 / 19:05

5 respostas

5

Acho que o maior gargalo é o número de processos que você gera. Aqui está um script simples que lista e filtra seu diretório em uma única passagem:

#!/usr/bin/perl

use strict;
use warnings;

my %files;
my $dir;
my @extensions = ("\.tis","\.are","LM\.bmp","\.wed");

opendir($dir, ".") || die "Error opening dir\n";
while (my $file = readdir($dir)) {
    foreach my $ext (@extensions) {
        if ($file =~ /^(.*)$ext$/sm) {
            $files{$1} += 1;
        }
    }
}
closedir($dir);

foreach my $file (keys %files) {
    if ($files{$file} == scalar(@extensions)) {
        print "$file\n";
    }
}
    
por 21.03.2017 / 19:20
6

Se eu entendi corretamente, você está procurando por todos os nomes de arquivos existentes em cada uma das extensões de destino. Se assim for, você poderia fazer:

( shopt -s nullglob; 
  for i in *.wed; do 
    set --  "${i//.wed}"{.tis,.are,LM.bmp}*; 
    [[ $# -eq 3 ]] && printf '%s\n'  "${i//.wed}"; 
done )

Explicação

  • shopt -s nullglob : esta é uma opção específica do bash que faz com que os globs se expandam para uma string nula em vez de si mesmos se nenhum arquivo correspondente for encontrado. O ( ) está lá apenas para garantir que a opção seja configurada apenas para este comando e não afetará o shell pai.
  • for i in *.wed; do ...; done : itera todos os arquivos ou diretórios no diretório atual cujo nome termina com .wed , salvando cada um como $i .
  • "${i//.wed}" : o nome do arquivo sem a extensão .wed .
  • set -- "${i//.wed}"{.tis,.are,LM.bmp}* : as chaves serão expandidas para foo.tis , foo.are etc, e o * é um truque para fazer com que o bash tente combiná-las como globs. Isso significa que só funcionará se o nome do arquivo atual existir.
  • [[ $# -eq 3 ]] && printf '%s\n' "${i//.wed}" : se houver exatamente 3 arquivos em $@ , so 3 além do original .wed , imprima o nome do arquivo sem a extensão.

Se você deseja imprimir todos os 4 nomes de arquivos, incluindo a extensão, basta alterar o printf '%s\n' "${i//.wed}" para printf '%s\n' "$i" .

    
por 21.03.2017 / 19:35
2
find . -type d  -exec sh -c '
   h=$1; cd "$h" || exit
   set -- /dev/nul[l] [f]oobar.{wed,tis,are} [f]oobarLM.bmp; shift
   case $# in 4 ) for arg; do printf "%s/%s\n" "$h" "$arg"; done ;; esac
' {} {} \;

N.B .: Aqui não vemos mais nada além desses 4 arquivos & no caso específico e somente quando ALL 4 são encontrados, ele os exibe, mesmo se outros arquivos foobar.XXX existissem no mesmo diretório. Porque o OP não foi muito claro sobre isso.

    
por 21.03.2017 / 19:50
1

Bash

Nós configuramos um loop for com glob expressão *LM.bm[p] , para que possamos desambiguar o cenário do arquivo com nome terminando em p de fato existente versus não. Esta é uma variedade básica de shopt nullglob . Se virmos um nome terminar em p = > o shell foi capaz de expandir esse glob, ou seja, tal arquivo existia. OTOH, se virmos um nome terminar em [p] = > o shell era NOT capaz de expandir o significado glob *LM.bm[p] , não havia nenhum arquivo com esse nome.

O próximo passo é cortar os 6 caracteres (L M. b m p) do nome do arquivo que agora sabemos que existe. Usando a parte restante do nome do arquivo, configuramos 3 parâmetros posicionais correspondentes às 3 extensões .wed .are .tis .

A chave é que, mesmo que uma das extensões não seja expandida, seu nome será exibido com o último caractere incluído em [ ] . = > Todos os 3 arquivos não estão todos presentes. = > pule isso . E a cláusula else = > Todos os 3 arquivos estão presentes + 1 (* LM.bmp) já está presente devido à nossa verificação prévia. Daí vemos todos os 4 arquivos e = > apto para impressão.

for i in *LM.bm[p]; do
   case $i in
      *[p] )
         f=${i%??????}
         set -- "$f".we[d] "$f".ar[e] "$f".ti[s]
         savIFS=$IFS
            IFS=/
            case "$*/" in
               *"]/"* ) : ;;
               *      ) printf '%s\n' "$f" ;;
            esac
         IFS=$savIFS
   esac
done
    
por 22.03.2017 / 09:43
0
perl -le '
   while ( <*LM.bmp> ) {
      (my $f = $_) =~ s|LM\.bmp$||;
      print $f if 3 == grep { -e $f . $_ } qw/.tis .are .wed/;
   }
'
    
por 21.03.2017 / 22:08