Estoy buscando un shell de una línea para encontrar el archivo más antiguo en un árbol de directorios.
Estoy buscando un shell de una línea para encontrar el archivo más antiguo en un árbol de directorios.
Respuestas:
Esto funciona (actualizado para incorporar la sugerencia de Daniel Andersson):
find -type f -printf '%T+ %p\n' | sort | head -n 1
find
está vacía debido al hecho de que el nombre del archivo contiene nueva línea.
Este es un poco más portátil y porque no depende de la find
extensión GNU -printf
, por lo que también funciona en BSD / OS X:
find . -type f -print0 | xargs -0 ls -ltr | head -n 1
El único inconveniente aquí es que está algo limitado al tamaño de ARG_MAX
(que debería ser irrelevante para la mayoría de los núcleos más nuevos). Entonces, si hay más de getconf ARG_MAX
caracteres devueltos (262,144 en mi sistema), no le da el resultado correcto. Tampoco es compatible con POSIX porque -print0
y xargs -0
no lo es.
Aquí se describen algunas soluciones más para este problema: ¿Cómo puedo encontrar el archivo más reciente (más nuevo, más antiguo, más antiguo) en un directorio? - Wiki de Greg
xargs: ls: terminated by signal 13
error como efecto secundario. Supongo que es SIGPIPE. No tengo idea de por qué no obtengo un error similar cuando canalizo la salida del tipo a la cabeza en mi solución.
head
creo que es el comando que se cierra una vez que ha leído una línea y "rompe" la tubería. No obtiene el error porque sort
no parece quejarse de ello, pero ls
sí en el otro caso.
xargs
necesitan invocar ls
más de una vez. En ese caso, los resultados ordenados de esas invocaciones múltiples terminan concatenados cuando deberían fusionarse.
ls
verlo y mirar el archivo más antiguo, su solución probablemente superará el límite de longitud de la línea de comando, provocando ls
que se invoque varias veces. Obtendrá la respuesta incorrecta, pero nunca lo sabrá.
Los siguientes comandos están garantizados para funcionar con cualquier tipo de nombre de archivo extraño:
find -type f -printf "%T+ %p\0" | sort -z | grep -zom 1 ".*" | cat
find -type f -printf "%T@ %T+ %p\0" | \
sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //'
stat -c "%y %n" "$(find -type f -printf "%T@ %p\0" | \
sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //')"
El uso de un byte nulo ( \0
) en lugar de un carácter de salto de línea ( \n
) asegura que la salida de find seguirá siendo comprensible en caso de que uno de los nombres de archivo contenga un carácter de salto de línea.
El -z
conmutador hace que tanto sort como grep interpreten solo bytes nulos como caracteres de fin de línea. Como no hay tal interruptor para la cabeza, usamos en su grep -m 1
lugar (solo una ocurrencia).
Los comandos están ordenados por tiempo de ejecución (medido en mi máquina).
El primer comando será el más lento, ya que primero tiene que convertir el mtime de cada archivo en un formato legible por humanos y luego ordenar esas cadenas. La tubería al gato evita colorear la salida.
El segundo comando es un poco más rápido. Si bien todavía realiza la conversión de fecha, la ordenación numérica ( sort -n
) de los segundos transcurridos desde la época de Unix es un poco más rápida. sed elimina los segundos desde la época de Unix.
El último comando no realiza ninguna conversión y debería ser significativamente más rápido que los dos primeros. El comando find en sí no mostrará el tiempo m del archivo más antiguo, por lo que se necesita stat.
Aunque la respuesta aceptada y otros aquí hacen el trabajo, si tiene un árbol muy grande, todos ellos ordenarán todo el conjunto de archivos.
Mejor sería si pudiéramos enumerarlos y hacer un seguimiento de los más antiguos, sin la necesidad de ordenarlos.
Por eso se me ocurrió esta solución alternativa:
ls -lRU $PWD/* | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { gsub(/-/,"",$6); if (substr($1,0,1)=="/") { pat=substr($1,0,length($0)-1)"/"; }; if( $6 != "") {if ( $6 < oldd ) { oldd=$6; oldf=pat$8; }; print $6, pat$8; count++;}} END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'
Espero que pueda ser de ayuda, incluso si la pregunta es un poco vieja.
Edición 1: estos cambios permiten analizar archivos y directorios con espacios. Es lo suficientemente rápido como para emitirlo en la raíz /
y encontrar el archivo más antiguo.
ls -lRU --time-style=long-iso "$PWD"/* | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { gsub(/-/,"",$6); if (substr($0,0,1)=="/") { pat=substr($0,0,length($0)-1)"/"; $6="" }; if( $6 ~ /^[0-9]+$/) {if ( $6 < oldd ) { oldd=$6; oldf=$8; for(i=9; i<=NF; i++) oldf=oldf $i; oldf=pat oldf; }; count++;}} END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'
Comando explicado:
Ejecutándolo:
~ $ time ls -lRU "$ PWD" / * | awk etc.
Fecha más antigua: 19691231
Archivo: /home/.../.../backupold/.../EXAMPLES/how-to-program.txt
Total comparado: 111438
0m1.135s reales
usuario 0m0.872s
sys 0m0.760s
EDITAR 2: Mismo concepto, mejor solución find
para mirar el tiempo de acceso (use %T
con el primero printf
para el tiempo de modificación o %C
para el cambio de estado ).
find . -wholename "*" -type f -printf "%AY%Am%Ad %h/%f\n" | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { if ($1 < oldd) { oldd=$1; oldf=$2; for(i=3; i<=NF; i++) oldf=oldf " " $i; }; count++; } END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'
EDITAR 3: el siguiente comando utiliza el tiempo de modificación y también imprime el progreso incremental a medida que encuentra archivos antiguos y antiguos, lo cual es útil cuando tiene algunas marcas de tiempo incorrectas (como 1970-01-01):
find . -wholename "*" -type f -printf "%TY%Tm%Td %h/%f\n" | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { if ($1 < oldd) { oldd=$1; oldf=$2; for(i=3; i<=NF; i++) oldf=oldf " " $i; print oldd " " oldf; }; count++; } END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'
ls
es malo para las secuencias de comandos, ya que su salida no está destinada a máquinas, el formato de salida varía según las implementaciones. Como ya dijiste, find
es bueno para las secuencias de comandos, pero también podría ser bueno agregar esa información antes de contar las ls
soluciones.
Utilice ls: la página de manual le indica cómo ordenar el directorio.
ls -clt | head -n 2
El -n 2 es para que no obtenga el "total" en la salida. Si solo quieres el nombre del archivo.
ls -t | head -n 1
Y si necesita la lista en el orden normal (obtener el archivo más reciente)
ls -tr | head -n 1
Mucho más fácil que usar find, mucho más rápido y más robusto: no tiene que preocuparse por los formatos de nombres de archivos. También debería funcionar en casi todos los sistemas.
find ! -type d -printf "%T@ %p\n" | sort -n | head -n1
sort -n
.
Parece que por "más viejo" la mayoría de la gente ha asumido que se refería al "tiempo de modificación más antiguo". Probablemente se haya corregido, de acuerdo con la interpretación más estricta de "más antiguo", pero en caso de que desee el que tenga el tiempo de acceso más antiguo , modificaría la mejor respuesta de esta manera:
find -type f -printf '%A+ %p\n' | sort | head -n 1
Note el %A+
.
set $(find /search/dirname -type f -printf '%T+ %h/%f\n' | sort | head -n 1) && echo $2
find ./search/dirname -type f -printf '%T+ %h/%f\n'
Imprime fechas y nombres de archivos en dos columnas.sort | head -n1
mantiene la línea correspondiente al archivo más antiguo.echo $2
muestra la segunda columna, es decir, el nombre del archivo.
find -type f -printf '%T+ %p\n' | sort | head -1