Tocando recursivamente na última datetime modificada dos diretórios como a mesma data e hora do último arquivo modificado dentro

1

Anteriormente, eu corri dos2unix em cada arquivo *.php no meu servidor de forma recursiva com este comando:

find . -name '*.php' -type f -exec dos2unix --keepdate {} +

Todos os arquivos processados por dos2unix tiveram sua data / hora modificada pela última vez intacta, mas o diretório pai que contém esses arquivos ainda tem sua data / hora modificada pela última vez atualizada para a hora em que executei o comando.

Como eu, com um único liner, toco os diretórios recursivamente, definindo o último horário modificado dos diretórios como o mesmo datetime do último arquivo modificado dentro do diretório?

A partir disso:

dir_a/               2013-05-13 14:05
   abc.php           2012-09-01 12:34
   def.php           2012-09-15 23:45
   dir_b/            2013-05-13 14:05
       uvw.php       2012-10-01 01:23
       xyz.php       2012-10-08 09:10

Para isso:

dir_a/               2012-09-15 23:45
   abc.php           2012-09-01 12:34
   def.php           2012-09-15 23:45
   dir_b/            2012-10-08 09:10
       uvw.php       2012-10-01 01:23
       xyz.php       2012-10-08 09:10

O servidor está rodando no CentOS 5.6.

    
por Phil 13.05.2013 / 11:57

2 respostas

2

Talvez algo assim:

find . -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f  -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";if [ -n "$file" ]; then touch "$dir" -mr "$file"; fi; done

Explicação:

Para passar a data de modificação de um arquivo para outro, precisamos executar:

touch file1 -mr file2

Primeiro, precisamos encontrar subdiretórios:

find . -type d -print0 | while read -r -d '' dir; do echo "$dir"; done

Nesse caso, a opção -exec ficaria muito complexa, por isso uso a abordagem de leitura por segundo. Você precisa certificar-se de enviar a saída de find com NUL como separador de registro e informar read para dividir isso ( -d '' ).

Para encontrar arquivos com a data da última modificação, posso usar:

find ./  -maxdepth 1 -type f  -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-

Combinando:

find . -mindepth 1  -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f  -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";echo "$dir <- $file"; done

Por fim, adicione o toque:

find . -mindepth 1  -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f  -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";touch "$dir" -mr "$file"; done

Eu uso a opção -mindepth aqui porque eu iniciei este comando a partir do diretório contendo dir_a que não possui nenhum arquivo para ler a data. Para eliminar esse problema, posso usar if-else para garantir que não tente ler o tempo de um arquivo não existente:

find . -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f  -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";if [ -n "$file" ]; then touch "$dir" -mr "$file"; fi; done
    
por 13.05.2013 / 14:13
1

Isso deve ser feito:

find -mindepth 1 -type d -print0 | while IFS= read -r -d '' dir; do 
 time=$(
  find "$dir" -mindepth 1 -maxdepth 1 -type f -exec stat -c %Y {} \; | 
  sort -nk2 | tail -n 1
 ); touch -d @$time "$dir"; 
 done

EXPLICAÇÃO:

  • find -mindepth 1 -type d | while IFS= read -r -d '' dir; do : itera todos os diretórios, exceto o atual ( -mindepth 1 ). O uso de um while loop com IFS= e o -r flag é para garantir que o comando funcione com nomes de diretório que contenham espaços ou novas linhas ( \n ) ou outros caracteres estranhos. O -d '' define o delimitador como o caractere nulo para poder trabalhar com a opção -print0 do find.

  • find $dir -mindepth 1 -maxdepth 1 -type f -exec stat --printf="%Y\n" {} \; : encontre todos os arquivos de cada diretório $dir e execute stat , que imprime a data de modificação em segundos desde a época .

  • sort -nk2 | tail -n 1 : isso apenas classifica as datas e mantém as mais recentes. As duas últimas partes combinadas fornecem a hora da modificação do arquivo mais recente que é salvo na variável $time .

  • touch -d @$time $dir : aqui usamos o comando touch para definir o tempo de modificação de cada diretório. O @ está lá para dizer touch que estamos dando tempo em segundos desde a época, isso é um recurso padrão GNU coreutils .

por 13.05.2013 / 14:22