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
findestá 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 findextensió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_MAXcaracteres devueltos (262,144 en mi sistema), no le da el resultado correcto. Tampoco es compatible con POSIX porque -print0y xargs -0no 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 13error 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.
headcreo 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 sortno parece quejarse de ello, pero lssí en el otro caso.
xargsnecesitan invocar lsmás de una vez. En ese caso, los resultados ordenados de esas invocaciones múltiples terminan concatenados cuando deberían fusionarse.
lsverlo y mirar el archivo más antiguo, su solución probablemente superará el límite de longitud de la línea de comando, provocando lsque 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 -zconmutador 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 1lugar (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 findpara mirar el tiempo de acceso (use %Tcon el primero printfpara el tiempo de modificación o %Cpara 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}'
lses 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, findes bueno para las secuencias de comandos, pero también podría ser bueno agregar esa información antes de contar las lssoluciones.
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