¿Cómo escribo un script para mover solo los 20 archivos más antiguos de una carpeta a otra? ¿Hay alguna manera de tomar los archivos más antiguos en una carpeta?
bash
shell
shell-script
files
timestamps
usuario11598
fuente
fuente
atime
(último acceso),ctime
(último cambio de permiso) ymtime
(última modificación) ... por ejemplo.ls -t
y del hallazgoprintf "%T"
usomtime
... Parece, de acuerdo con este enlace , que misext4
particiones son capaces de manejar una fecha de creación, perols
efind
ystat
no tienen las opciones apropiadas (todavía) ...Respuestas:
Analizar la salida de no
ls
es confiable .En su lugar, utilice
find
para localizar los archivos ysort
ordenarlos por marca de tiempo. Por ejemplo:¿Qué está haciendo todo esto?
Primero, los
find
comandos localizan todos los archivos y directorios en el directorio actual (.
), pero no en subdirectorios del directorio actual (-maxdepth 1
), luego imprime:La marca de tiempo es importante. El
%T@
especificador de formato-printf
se desglosa enT
, que indica "Tiempo de última modificación" del archivo (mtime) y@
, que indica "Segundos desde 1970", incluidos los segundos fraccionarios.El espacio es simplemente un delimitador arbitrario. La ruta completa al archivo es para que podamos consultarlo más tarde, y el carácter NULL es un terminador porque es un carácter ilegal en un nombre de archivo y, por lo tanto, nos permite saber con seguridad que llegamos al final de la ruta al archivo.
Lo he incluido
2>/dev/null
para que se excluyan los archivos a los que el usuario no tiene permiso de acceso, pero se suprimen los mensajes de error sobre su exclusión.El resultado del
find
comando es una lista de todos los directorios en el directorio actual. La lista se canaliza a lasort
que se indica que:-z
Trate NULL como el carácter terminador de línea en lugar de nueva línea.-n
Ordenar numéricamenteComo los segundos desde 1970 siempre suben, queremos el archivo cuya marca de tiempo sea el número más pequeño. El primer resultado
sort
será la línea que contiene la marca de tiempo numerada más pequeña. Todo lo que queda es extraer el nombre del archivo.Los resultados de la
find
,sort
la tubería se pasa a través de la sustitución de proceso awhile
donde se lee como si fuera un archivo en la entrada estándar.while
a su vez invocaread
para procesar la entrada.En el contexto de
read
establecemos laIFS
variable en nada, lo que significa que los espacios en blanco no se interpretarán inapropiadamente como un delimitador.read
se cuenta-r
, que desactiva la expansión de escape, y-d $'\0'
, lo que hace que el NULL delimitador de fin de línea, haciendo coincidir la salida de nuestrafind
,sort
tubería.La primera porción de datos, que representa la ruta de archivo más antigua precedida por su marca de tiempo y un espacio, se lee en la variable
line
. A continuación, la sustitución de parámetros se usa con la expresión#*
, que simplemente reemplaza todos los caracteres desde el comienzo de la cadena hasta el primer espacio, incluido el espacio, sin nada. Esto elimina la marca de tiempo de modificación, dejando solo la ruta completa al archivo.En este punto, el nombre del archivo está almacenado
$file
y puede hacer lo que quiera con él. Cuando termine de hacer algo con$file
lawhile
instrucción,read
se ejecutará un bucle y el comando se ejecutará nuevamente, extrayendo el siguiente fragmento y el siguiente nombre de archivo.¿No hay una manera más simple?
No. Las formas más simples son defectuosas.
Si usa
ls -t
y canaliza haciahead
otail
(o cualquier cosa ), romperá los archivos con nuevas líneas en los nombres de archivo. Simv $(anything)
luego los archivos con espacios en blanco en el nombre causarán rotura. Simv "$(anything)"
luego los archivos con líneas nuevas en el nombre causarán roturas. Siread
no-d $'\0'
, entonces usted va a romper en archivos con espacios en blanco en sus nombres.Quizás en casos específicos usted sabe con certeza que una forma más simple es suficiente, pero nunca debe escribir suposiciones como esa en los scripts si puede evitar hacerlo.
Solución
Llamar como:
Para mover los 20 archivos más antiguos de
/var/log/foo/
a/mnt/backup/
.Tenga en cuenta que estoy incluyendo archivos y directorios. Para archivos solo agregue
-type f
a lafind
invocación.Gracias
Gracias a enzotib y Павел Танков por las mejoras a esta respuesta.
fuente
-n
. Al menos en mi versión, no ordena los números decimales correctamente. Debe eliminar el punto en la fecha o el uso-printf '%TY-%Tm-%TdT%TH:%TM:%TS %p\0' | sort -rz
, las fechas ISO u otra cosa..
debe ser irrelevante para usted). Sería más claro decirlosort -z -n -t. -k1
.%TS
también muestra una "parte fraccional" que estaría en la forma00.0000000000
, por lo que también pierde granularidad. GNU recientesort
podría resolver este problema mediante el uso-V
de un "tipo de versión", que manejará este tipo de coma flotante como se esperaba.%T@
también funcionaría, entonces, porque está rellenada con ceros.Es más fácil en zsh, donde puede usar el
Om
calificador global para ordenar las coincidencias por fecha (la más antigua primero) y el[1,20]
calificador para retener solo las primeras 20 coincidencias:Agregue el
D
calificador si también desea incluir archivos de puntos. Agregue.
si desea hacer coincidir solo archivos normales y no directorios.Si no tiene zsh, aquí hay un Perl one-liner (puede hacerlo en menos de 80 caracteres, pero a un costo adicional en claridad):
Con solo las herramientas POSIX o incluso bash o ksh, ordenar archivos por fecha es una molestia. Puede hacerlo fácilmente
ls
, pero analizar el resultado dels
es problemático, por lo que esto solo funciona si los nombres de los archivos contienen solo caracteres imprimibles distintos de las nuevas líneas.fuente
Combinar
ls -t
salida contail
ohead
.Ejemplo simple, que funciona solo si todos los nombres de archivo contienen solo caracteres imprimibles que no sean espacios en blanco y
\[*?
:fuente
ls -1Atr
touch $'foo\n*'
. ¿Qué sucede si ejecuta mv "$ (ls)" con ese archivo allí?Puede usar GNU find para esto:
Donde find imprime el tiempo de modificación (en segundos desde 1970) y el nombre de cada archivo del directorio actual, la salida se ordena según el primer campo, los 20 más antiguos se filtran y se mueven
dest_dir
. Elimine elecho
si ha probado la línea de comando.fuente
Nadie ha publicado (todavía) un ejemplo de bash que atienda caracteres de nueva línea incrustados (incrustado cualquier cosa) en el nombre del archivo, así que aquí hay uno. Mueve los 3 archivos regulares más antiguos (mdate)
Este es el fragmento de datos de prueba
Aquí está el fragmento de verificación de resultados
fuente
Es más fácil hacerlo con GNU
find
. Lo uso todos los días en mi DVR de Linux para eliminar grabaciones de mi sistema de videovigilancia anteriores a un día.Aquí está la sintaxis:
Recuerde que
find
define un día como 24 horas desde el momento de la ejecución. Por lo tanto, los archivos modificados por última vez a las 11 p. M. No se eliminarán a la 1 a. M.Incluso puede combinar
find
concron
, por lo que las eliminaciones se pueden programar automáticamente ejecutando el siguiente comando como root:Siempre puede obtener más información
find
al consultar su página de manual:fuente
Como las otras respuestas no se ajustan a mi propósito y las preguntas, este shell se prueba en CentOS 7:
fuente