Como altero os carimbos de data e hora da pasta recursivamente para o arquivo mais recente?

15

Eu queria saber se alguém sabe como alterar os carimbos de data / hora das pastas recursivamente com base no carimbo de data e hora mais recente encontrado nos arquivos dessa pasta.

Então, por exemplo:

jon @ UbuntuPanther: / media / media / MP3s / Foo Fighters / (1997-05-20) A cor e a forma $ ls -alF
55220 total
drwxr-xr-x 2 jon jon 4096 30-08-2010 12:34 ./
drwxr-xr-x 11 jon jon 4096 30-08-2010 12:34 ../
-rw-r - r-- 1 jon jon 1694044 2010-04-18 00:51 Foo Fighters - Doll.mp3
-rw-r - r-- 1 jon jon 3151170 2010-04-18 00:51 Foo Fighters - Enough Space.mp3
-rw-r - r-- 1 jon jon 5004289 2010-04-18 00:52 Foo Fighters - Everlong.mp3
-rw-r - r-- 1 jon jon 5803125 2010-04-18 00:51 Foo Fighters - fevereiro Stars.mp3
-rw-r - r-- 1 jon jon 4994903 2010-04-18 00:51 Foo Fighters - Hey, Johnny Park! .mp3
-rw-r - r-- 1 jon jon 4649556 2010-04-18 00:52 Foo Fighters - Monkey Wrench.mp3
-rw-r - r-- 1 jon jon 5216923 2010-04-18 00:51 Foo Fighters - My Hero.mp3
-rw-r - r-- 1 jon jon 4294291 2010-04-18 00:52 Foo Fighters - My Poor Brain.mp3
-rw-r - r-- 1 jon jon 6778011 2010-04-18 00:52 Foo Fighters - New Way Home.mp3
-rw-r - r-- 1 jon jon 2956287 2010-04-18 00:51 Foo Fighters - See You.mp3
-rw-r - r-- 1 jon jon 2730072 2010-04-18 00:51 Foo Fighters - Up in Arms.mp3
-rw-r - r-- 1 jon jon 6086821 2010-04-18 00:51 Foo Fighters - Walking After You.mp3
-rw-r - r-- 1 jon jon 3033660 2010-04-18 00:52 Foo Fighters - Wind Up.mp3

A pasta "(20/05/1997), A Cor e a Forma", teria seu carimbo de data e hora definido para 18/04/2010 00:52.

MonkeyWrench32
fonte

Respostas:

20

Você pode usar touch -ro carimbo de data e hora de outro arquivo em vez da hora atual (ou touch --reference=FILE)

Aqui estão duas soluções. Em cada solução, o primeiro comando altera o tempo de modificação do diretório para o do arquivo mais novo imediatamente abaixo dele, e o segundo comando examina a árvore de diretórios inteira recursivamente. Mude para o diretório ( cd '.../(1997-05-20) The Colour and The Shape') antes de executar qualquer um dos comandos.

No zsh (remova os Darquivos de ponto para ignorar):

touch -r *(Dom[1]) .
touch -r **/*(Dom[1]) .

No Linux (ou mais geralmente com o GNU find):

touch -r "$(find -mindepth 1 -maxdepth 1 -printf '%T+=%p\n' |
            sort |tail -n 1 | cut -d= -f2-)" .
touch -r "$(find -mindepth 1 -printf '%T+=%p\n' |
            sort |tail -n 1 | cut -d= -f2-)" .

No entanto, observe que esses não assumem caracteres de nova linha nos nomes dos arquivos.

Gilles 'SO- parar de ser mau'
fonte
Os comandos funcionam (eu usei os do Linux), mas não está funcionando recursivamente. Eu o executei na minha pasta raiz (/ media / media / MP3s) e não tive tanta sorte com o restante dos diretórios de artistas. Obrigado por sua ajuda até agora.
precisa saber é o seguinte
@ MonkeyWrench32: Eu respondi sua pergunta como eu a entendi. Deseja aplicar o comando a todos os diretórios abaixo /media/media/MP3s? Então, em zsh: for d in /media/media/MP3s/**/*(/); do touch -r $d/*(Dom[1]) $d; done. Sem o zsh (mas, na verdade, use o zsh, é mais simples): coloque o comando em um script e execute find /media/media/MP3s -type d -execdir /path/to/the/script \;.
Gilles 'SO- stop be evil'
Perfeição! Você é um mestre do zsh! : D
MonkeyWrench32
Se você usar for d in ...como pode adaptar o seguinte, também funcionará com pastas que contêm espaços? (que ainda está faltando na minha solução)
rubo77
@ rubo77 Não entendo do que você está falando. Todas as soluções que publiquei funcionam em nomes de arquivos que contêm espaços (alguns deles falham em nomes de arquivos que contêm novas linhas). Observe que algumas das minhas soluções requerem zsh, o que não requer aspas duplas em torno das substituições de variáveis.
Gilles 'SO- stop be evil'
3

Isso não é "recursivamente", apenas altera todos os carimbos de data e hora em uma pasta. Se é isso que você quer dizer, há duas etapas.

stat -c '%Y' filenameirá imprimir o timestamp de filenamee stat -c '%Y %n' *saída será a hora eo nome do arquivo de cada arquivo na pasta, então isso vai encontrar o nome do arquivo mais recentemente modificado na pasta atual:

mostrecent="`stat -c '%Y %n' * | sort -n | tail -n1 | cut -d ' ' -f '2-'`"

Pensando bem, há uma maneira mais fácil de obter o carimbo de data / hora mais alto na pasta:

mostrecent="`ls -t | head -n1`"

Então você deseja alterar todos os arquivos na pasta para ter o mesmo carimbo de data e hora desse arquivo. touch -r foo barmudará barpara ter o mesmo carimbo de data / hora modificado que foo, portanto, todos os arquivos da pasta mudarão para o mesmo carimbo de data / hora modificado que o arquivo modificado mais recentemente:

touch -r "$mostrecent" *

Portanto, o one-liner é:

touch -r "`ls -t | head -n1`" *
Michael Mrozek
fonte
Analisar a saída de ls é uma má idéia , embora eu admita que é um caso tentador. Ainda é arriscado se lsmanipular nomes de arquivos contendo caracteres não ASCII. Se você quiser one-liners, use zsh :-)
Gilles 'SO- stop be evil'
@ Gilles É, mas ainda estou com problemas para fazê-lo; Suponho que meus nomes de arquivos não contenham caracteres não ASCII. Sua resposta é melhor ainda
Michael Mrozek
2

Eu montei o trabalho e agora:

Este seria um script que altera todos os diretórios internos /tmp/test/para o registro de data e hora do arquivo mais recente dentro de cada diretório:

#!/bin/bash
if [ -z "$1" ] ; then
  echo 'ERROR: Parameter missing. specify the folder!'
  exit
fi
#MODE=tail # change to newest file
MODE=head # change to oldest file
for d in "$1"/*/; do
  echo "running on $d"
  find "$d" -type d -execdir \
    echo touch --reference="$(find "$d" -mindepth 1 -maxdepth 1 -printf '%T+=%p\n' \
                              | sort | "$MODE" -n 1 | cut -d= -f2-)" "$d" \;
    # remove echo to really run it
done

você pode adicionar alguns arquivos de teste em / tmp assim:

mkdir /tmp/test
cd /tmp/test
mkdir d1
mkdir d2
touch d1/text1.txt
sleep 1
touch d1/movie1.mov
touch d2/movie2.mov
sleep 1
touch d2/text2.txt
touch notthis.file
rubo77
fonte
1
obrigado pela sua edição, ele funciona! embora o eco remova o "novo, primeiro parece que está faltando, mas funciona.
rubo77
2

Apenas use

find . -exec touch {} \;
Daz
fonte
0

Eu uso esse script para definir recursivamente os carimbos de data e hora da pasta para o conteúdo mais recente. (É muito semelhante à resposta de Gilles):

#! /bin/bash

# Change mtime of directories to that of latest file (or dir) under it, recursively
# Empty leaf directories, or dirs with only symlinks get the $default_timestamp

default_timestamp='1980-01-01 00:00:00'

dir="$1"

[ -d "$dir" ] || { echo "Usage: $0 directory" >&2; exit 1; }

find "$dir" -depth -type d | while read d; do
    latest=$(find "$d" -mindepth 1 -maxdepth 1 \( -type f -o -type d \) -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)
    if [ -n "$latest" ]; then
        touch -c -m -r "$latest" "$d" \
            || echo "ERROR setting mtime of '$d' using ref='$f'" >&2
    else
        touch -c -m -d "$default_timestamp" "$d" \
            || echo "ERROR setting mtime of '$d' to default '$default_timestamp'" >&2
    fi
done
mivk
fonte
0

Peguei o comando zG do @Gilles e o aprimorei para trabalhar em subpastas, mas parece que o zsh é terrivelmente ineficiente para a parte ** / * (FDod).

# Don't do this
for d in **/*(FDod); do touch -r "$d"/*(Dom[1]) "$d"; done

As aspas permitem que as entradas do diretório que contêm espaços e guias funcionem corretamente. O FD faz com que ele encontre diretórios não vazios, incluindo os que começam com., O od faz com que seja pesquisado da maneira mais profunda , para que os registros de data e hora da pasta pai sejam atualizados corretamente.

Ao testar, notei que o desempenho e a pegada de memória para ** / * (FDod) eram insanos (mais de 1,4 GB para apenas pastas de 650k) e liam todo o conteúdo antes de começar a tocar nas pastas. Depois de substituí-lo por localizar / ler, ficou muito mais rápido, não queimava memória e começou quase imediatamente.

#! /usr/bin/env zsh
# Do this instead
find "$@" -depth -type d ! -empty -print0 |while IFS= read -r -d ''; do
    touch -r "$REPLY"/*(Dom[1]) "$REPLY"
done

Se você não o estiver executando em um script, substitua "$ @" pelas pastas das quais deseja executá-lo.

Aaron Queenan
fonte
-1

Eu realmente não gostei da obscuridade dos comandos do shell e fiz isso rapidamente em python.

Ele define recursivamente todos os mtimes do diretório para o mais novo arquivo não excluído / dir mtime.

import os
import sys

EXCLUDE_FILES = ['.DS_Store']
EXCLUDE_DIRS = ['.git', '.hg']

def inheritMTime(path):
    for root, dirs, files in os.walk(path, topdown=False, followlinks=False):
        currentMaxMtime = None
        for subpath in files:
            if subpath in EXCLUDE_FILES:
                print "ignore file", os.path.join(root, subpath)
                continue
            currentMaxMtime = max(currentMaxMtime, os.lstat(os.path.join(root, subpath)).st_mtime)
        for subpath in dirs:
            if subpath in EXCLUDE_DIRS:
                print "ignore dir", os.path.join(root, subpath)
                continue
            currentMaxMtime = max(currentMaxMtime, os.lstat(os.path.join(root, subpath)).st_mtime)
        if currentMaxMtime is not None:
            os.utime(root, (currentMaxMtime, currentMaxMtime))

if __name__ == "__main__":
    for dir in os.listdir(sys.argv[1]):
        if os.path.isdir(dir):
            current = os.path.join(sys.argv[1], dir)
            inheritMTime(current)
sleeplessnerd
fonte