Encontre as últimas versões de vários arquivos em vários diretórios

1

Estou escrevendo um aplicativo e um dos pré-requisitos é poder examinar vários diretórios e encontrar a versão mais recente de cada arquivo.

Eu obtive sucesso com ls e find para obter os arquivos mais recentes, mas não a versão mais recente do arquivo EACH, se esses mesmos arquivos estiverem localizados em vários diretórios. Uma das ressalvas é que eu não necessariamente saberá o que os arquivos são chamados, mas saberão os nomes dos diretórios.

Exemplo: DIR1, DIR2 e DIR3 contêm uma versão do FileA e do FileB. Preciso das versões mais recentes do FileA e do FileB contidas em todos os três (ou mais) diretórios.

Alguém tem alguma ideia?

    
por user1146368 20.12.2014 / 03:33

2 respostas

2

Você está no caminho certo com sua escolha de ferramentas:

  • ls -t é uma boa maneira de classificar arquivos ordenados por horário, para que você possa escolher as últimas
  • find é a ferramenta certa para encontrar arquivos que correspondam a algum padrão em diretórios e subdiretórios

A parte complicada do curso é que você precisa de algum tipo de agrupamento por nome de arquivo, e escolha o arquivo mais recente em cada grupo. Por causa desse requisito, Eu acho que você precisa de um loop onde você itera sobre cada nome de arquivo de destino para encontrar sua última versão.

Supondo que os arquivos estejam em $dir1 , $dir2 ou $dir3 , você poderia escrever uma função para encontrar a versão mais recente de algum padrão como este:

find_latest() {
    pattern=$1
    ls -t "$dir1/$pattern" "$dir2/$pattern" "$dir3/$pattern" | head -n 1
}

Então, digamos que você tenha os padrões access.log , error.log , x* , então você pode passar por cima deles assim, por exemplo:

for pattern in access.log error.log 'x*'; do
    latest=$(find_latest 'a*')
    echo $latest
done

Se a suposição acima não for verdadeira, e os arquivos podem estar em subdiretórios de $dir1 , $dir2 ou $dir3 , então você precisa usar find , fica um pouco mais complexo:

find_latest() {
    pattern=$1
    find "$dir1" "$dir2" "$dir3" -name "$pattern" -print0 | xargs -0 ls -t | head -n 1
}

Há uma pequena advertência: se um caminho contiver caracteres de nova linha, essa função não funcionará bem porque a etapa head -n 1 irá cortar a parte do caminho após a nova linha. Eu cruzo meus dedos que você não tem esses caminhos; -)

    
por 20.12.2014 / 12:17
0

Você pode fazer isso com pax :

pax -wrtvZs"|.*/||p" ./DIR[123] "$PWD"

Então, vou tentar dividi-lo por argumento:

  • -wr - estes são w rite e r ead e, juntos, eles significam que pax deve copiar arquivos em vez de arquivá-los. Você também pode abrir a cópia e apenas criar hardlinks com -l .

  • -t - redefine todos os tempos de acesso a arquivos ao estado que tinham antes de pax os lerem para verificar seus metadados.

  • -v - funciona com detalhes.

  • -Z - não compara os tempos de modificação dos arquivos de origem até que todas as possíveis substituições de nomes sejam concluídas.

    • É isso (e a próxima coisa) que torna isso tão fácil. Sem isso - e provavelmente o problema que você terá em outro lugar - é que DIR1/FILEA e DIR2/FILEA são arquivos diferentes, mesmo que compartilhem um nome de base. E então eles nunca são comparados sem isso e ...
  • -s - substitua e substitua porções de um nome de arquivo por um padrão sed regexp.

    • Aqui, apenas reduzo todas as partes de todos os arquivos para seus nomes de base, portanto, -Z se aplica a todos FILEA s e somente o mais recente é copiado em "$PWD" .

Eu usei o teste a seguir para verificar tudo isso:

for d in DIR3 DIR1 DIR2
do  cd ~; mkdir -p "$d"; cd "$d"
    sleep 90; touch FILEB FILEA
done; cd ~

... que recebe o conjunto de testes. Aqui estão os tempos de modificação resultantes:

ls -l ./DIR[123]/FILE[AB]                                
-rw-r--r-- 1 mikeserv mikeserv 0 Dec 20 03:28 ./DIR1/FILEA
-rw-r--r-- 1 mikeserv mikeserv 0 Dec 20 03:28 ./DIR1/FILEB
-rw-r--r-- 1 mikeserv mikeserv 0 Dec 20 03:29 ./DIR2/FILEA
-rw-r--r-- 1 mikeserv mikeserv 0 Dec 20 03:29 ./DIR2/FILEB
-rw-r--r-- 1 mikeserv mikeserv 0 Dec 20 03:26 ./DIR3/FILEA
-rw-r--r-- 1 mikeserv mikeserv 0 Dec 20 03:26 ./DIR3/FILEB

E, quando eu corro:

pax -wrtvZs"|.*/||p" ./DIR[123] "$PWD"
ls -l ./FILE[AB]

... a saída é ...

./DIR1/FILEA >> FILEA
/home/mikeserv/FILEA
./DIR1/FILEB >> FILEB
/home/mikeserv/FILEB
./DIR2/FILEA >> FILEA
/home/mikeserv/FILEA
./DIR2/FILEB >> FILEB
/home/mikeserv/FILEB
./DIR3/FILEA >> FILEA
./DIR3/FILEB >> FILEB

-rw-r--r-- 1 mikeserv mikeserv 0 Dec 20 03:29 ./FILEA
-rw-r--r-- 1 mikeserv mikeserv 0 Dec 20 03:29 ./FILEB

Você pode ver isso acontecer. Quando -s|||p altera um nome de arquivo, o modificador p imprime uma mensagem para stderr . Portanto, vemos que os arquivos DIR1 são avaliados primeiro - e copiados em $PWD , e os arquivos DIR2 recebem o mesmo tratamento - mas os arquivos DIR3 não são copiados porque $PWD/FILE[AB] são mais novos que agora.

    
por 20.12.2014 / 13:38

Tags