¿Cómo cambio las marcas de tiempo de la carpeta de forma recursiva al archivo más nuevo?

15

Me preguntaba si alguien sabe cómo cambiar las marcas de tiempo de las carpetas de forma recursiva en función de la última marca de tiempo encontrada de los archivos en esa carpeta.

Así por ejemplo:

jon @ UbuntuPanther: / media / media / MP3s / Foo Fighters / (1997-05-20) El color y la forma $ ls -alF
total 55220
drwxr-xr-x 2 jon jon 4096 2010-08-30 12:34 ./
drwxr-xr-x 11 jon jon 4096 2010-08-30 12:34 ../
-rw-r - r-- 1 jon jon 1694044 18/04/2010 00:51 Foo Fighters - Doll.mp3
-rw-r - r-- 1 jon jon 3151170 18/04/2010 00:51 Foo Fighters - Suficiente espacio.mp3
-rw-r - r-- 1 jon jon 5004289 18/04/2010 00:52 Foo Fighters - Everlong.mp3
-rw-r - r-- 1 jon jon 5803125 18/04/2010 00:51 Foo Fighters - February Stars.mp3
-rw-r - r-- 1 jon jon 4994903 18/04/2010 00:51 Foo Fighters - ¡Hola, Johnny Park! .mp3
-rw-r - r-- 1 jon jon 4649556 18/04/2010 00:52 Foo Fighters - Monkey Wrench.mp3
-rw-r - r-- 1 jon jon 5216923 18/04/2010 00:51 Foo Fighters - My Hero.mp3
-rw-r - r-- 1 jon jon 4294291 18/04/2010 00:52 Foo Fighters - My Poor Brain.mp3
-rw-r - r-- 1 jon jon 6778011 18/04/2010 00:52 Foo Fighters - New Way Home.mp3
-rw-r - r-- 1 jon jon 2956287 18/04/2010 00:51 Foo Fighters - Nos vemos.mp3
-rw-r - r-- 1 jon jon 2730072 18/04/2010 00:51 Foo Fighters - Up in Arms.mp3
-rw-r - r-- 1 jon jon 6086821 18/04/2010 00:51 Foo Fighters - Walking After You.mp3
-rw-r - r-- 1 jon jon 3033660 18/04/2010 00:52 Foo Fighters - Wind Up.mp3

La carpeta "(1997-05-20) The Color and The Shape" tendría su marca de tiempo establecida en 2010-04-18 00:52.

MonkeyWrench32
fuente

Respuestas:

20

Puede usar touch -rpara usar la marca de tiempo de otro archivo en lugar de la hora actual (o touch --reference=FILE)

Aquí hay dos soluciones. En cada solución, el primer comando cambia el tiempo de modificación del directorio al del archivo más nuevo inmediatamente debajo de él, y el segundo comando mira el árbol de directorios completo de forma recursiva. Cambie al directorio ( cd '.../(1997-05-20) The Colour and The Shape') antes de ejecutar cualquiera de los comandos.

En zsh (elimine el Dpara ignorar los archivos de puntos):

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

En Linux (o más generalmente con 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-)" .

Sin embargo, tenga en cuenta que esos no asumen caracteres de nueva línea en los nombres de archivo.

Gilles 'SO- deja de ser malvado'
fuente
Los comandos funcionan (utilicé los de Linux) pero no funciona de forma recursiva. Lo ejecuté en mi carpeta raíz (/ media / media / MP3s) y no tuve tanta suerte con el resto de los directorios de artistas allí. Gracias por su ayuda hasta ahora.
MonkeyWrench32
@ MonkeyWrench32: Respondí tu pregunta tal como la entendí. ¿Desea aplicar el comando a cada directorio debajo /media/media/MP3s? Luego, en zsh: for d in /media/media/MP3s/**/*(/); do touch -r $d/*(Dom[1]) $d; done. Sin zsh (pero realmente, use zsh, es más simple): ponga el comando en un script y ejecútelo find /media/media/MP3s -type d -execdir /path/to/the/script \;.
Gilles 'SO- deja de ser malvado'
¡Perfección! Eres un maestro zsh! : D
MonkeyWrench32
Si usa, for d in ...¿cómo puede adaptar lo siguiente para que también funcione con carpetas que contienen espacios? (eso todavía falta en mi solución)
rubo77
@ rubo77 No entiendo de qué estás hablando. Todas las soluciones que publiqué funcionan en nombres de archivos que contienen espacios (algunos de ellos fallan en los nombres de archivos que contienen nuevas líneas). Tenga en cuenta que algunas de mis soluciones requieren zsh, que no requiere comillas dobles alrededor de sustituciones variables.
Gilles 'SO- deja de ser malvado'
3

Eso no es "recursivamente", solo está cambiando todas las marcas de tiempo en una carpeta. Si eso es lo que quieres decir, hay dos pasos.

stat -c '%Y' filenamegenerará la marca de tiempo de filename, y stat -c '%Y %n' *generará la marca de tiempo y el nombre de archivo de cada archivo en la carpeta, por lo que encontrará el nombre de archivo del archivo modificado más recientemente en la carpeta actual:

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

Pensándolo bien, hay una manera más fácil de obtener la marca de tiempo más alta en la carpeta:

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

Luego, desea cambiar todos los archivos de la carpeta para que tengan la misma marca de tiempo que ese archivo. touch -r foo barcambiará barpara tener la misma marca de tiempo modificada que foo, por lo que esto cambiará todos los archivos de la carpeta para que tengan la misma marca de tiempo modificada que el archivo modificado más recientemente:

touch -r "$mostrecent" *

Entonces, la frase es:

touch -r "`ls -t | head -n1`" *
Michael Mrozek
fuente
Analizar la salida de ls es una mala idea , aunque admito que es un caso tentador. Todavía es arriesgado si lsmanipula nombres de archivos que contienen caracteres que no son ASCII. Si quieres frases ingeniosas, usa zsh :-)
Gilles 'SO- deja de ser malvado'
@Gilles Lo es, pero todavía tengo problemas para hacerlo; Supongo que porque mis nombres de archivo no contienen caracteres no ASCII. Sin embargo
Michael Mrozek
2

Puse el trabajo juntos y ahora:

Este sería un script que cambia todos los directorios dentro /tmp/test/de la marca de tiempo del archivo más nuevo dentro de cada directorio:

#!/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

puedes agregar algunos archivos de prueba en / tmp como este:

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
fuente
1
gracias por tu edición, ¡funciona! aunque el eco elimina "nuevamente, primero parece que falta, pero funciona.
rubo77
2

Solo usa

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

Utilizo este script para configurar recursivamente las marcas de tiempo de la carpeta con el contenido más reciente. (Es muy similar a la respuesta 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
fuente
0

Tomé el comando @Gilles zsh y lo mejoré para que funcione en subcarpetas, pero parece que zsh es terriblemente ineficiente para la parte ** / * (FDod).

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

Las comillas permiten que las entradas de directorio que contienen espacios y pestañas funcionen correctamente. El FD hace que encuentre directorios no vacíos, incluidos los que comienzan con., El od lo hace buscar en profundidad , de modo que las marcas de tiempo de la carpeta principal se actualicen correctamente.

Al realizar la prueba, noté que el rendimiento y la huella de memoria para ** / * (FDod) era una locura (más de 1.4 GB para solo 650k carpetas), y leyó todo antes de comenzar a tocar las carpetas. Después de reemplazarlo con buscar / leer, se volvió mucho más rápido, no quemó memoria y comenzó casi de inmediato.

#! /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

Si no lo está ejecutando en un script, reemplace "$ @" con las carpetas desde las que desea ejecutarlo.

Aaron Queenan
fuente
-1

Realmente no me gustó la oscuridad de los comandos de shell y rápidamente lo hice en python.

Establece recursivamente todos los directorios mtimes en el archivo / directorio no excluido más nuevo dentro de 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)
insomnio
fuente