¿Script para monitorear la carpeta de nuevos archivos?


127

¿Cómo detectar nuevos archivos en una carpeta con un script ? Me gustaría procesar los archivos tan pronto como se creen en la carpeta. ¿Es posible hacerlo o tengo que programar un script con que busque nuevos archivos cada minuto más o menos?


1
¿Va a eliminar archivos de la carpeta una vez que se procesan?
ztank1013

Respuestas:


151

Debería considerar usar inotifywait, como ejemplo:

inotifywait -m /path -e create -e moved_to |
    while read path action file; do
        echo "The file '$file' appeared in directory '$path' via '$action'"
        # do something with the file
    done

En Ubuntu inotifywaites proporcionado por el inotify-toolspaquete. A partir de la versión 3.13 (actual en Ubuntu 12.04) inotifywaitincluirá el nombre de archivo sin la opción -f. Las versiones anteriores pueden necesitar ser coaccionadas. Es importante tener en cuenta que la -eopción inotifywaites la mejor manera de filtrar eventos. Además, su readcomando puede asignar la salida posicional en múltiples variables que puede elegir usar o ignorar. No es necesario usar grep / sed / awk para preprocesar la salida.


1
¡Excelente! El inotifywaitera justo lo que quería.
ihatetoregister

2
Solo quiero actualizar esto. No necesitas awk para lograr esto. puede filtrar los eventos con '-e create' y obtener solo el nombre de archivo haciendo '-f% f' o la ruta completa usando '-f% w% f'. Entonces la primera línea del script anterior se convierte en: inotifywait -m / path -f% w% f -e create |
Lugoues

2
@Lugoues y ahora cuando intentas usar -f obtienes The '--filename' option no longer exists. The option it enabled in earlier versions of inotifywait is now turned on by default.So, solo tienes que hacerlo inotifywait -m /path -e create |voy a tratar de editar esta respuesta.
Bruno Bronosky

1
Ahora también hay una herramienta portátil para ello llamada fswatch. No lo escribí, pero es de código abierto y lo uso.

1
@Wender inotfiywait genera 3 piezas de información en una sola línea cuando se activa. El bash 'leer' incorporado lee la línea de entrada y asigna cada una de las tres piezas de información a una variable. Por lo tanto, la primera pieza se asigna a la ruta variable, la segunda a la acción y la tercera a la carpeta. Una vez asignados los valores a esas variables, están disponibles para su uso posterior (como en la línea de eco). Más información: tldp.org/LDP/Bash-Beginners-Guide/html/sect_08_02.html
Tim


24

Acabo de preparar esto y no veo grandes problemas con él, aparte de una pequeña posibilidad de que falten archivos entre las comprobaciones.

while true
do
       touch  ./lastwatch
       sleep 10
       find /YOUR/WATCH/PATH -cnewer ./lastwatch -exec SOMECOMMAND {} \;
done

Si el procesamiento de su archivo no tarda demasiado, no debe perderse ningún archivo nuevo. También podría poner en segundo plano las actividades ... No es a prueba de balas, pero sirve para algunos fines sin herramientas externas como inotify.


Buena atrapada. Lo mejoré un poco para admitir espacios en los nombres de archivo.
Michael Sacchi

Absolutamente. Ese es el camino a seguir. No estoy seguro de por qué me fui por ese camino, uso -exec de forma rutinaria.
Michael Sacchi

No es en tiempo real. el tiempo real siempre es mejor
Farhan

3
La mejor solución si inotifyno está disponible. Agregaría -type fpara filtrar solo archivos. De lo contrario, la carpeta también será devuelta.
Xiao Peng - ZenUML.com

Sí, la -f filenameopción es genial. Entonces, la única pregunta que queda es cómo hacer que esto comience al reiniciar. Voy a usar esto con mi planta solar para os.system("ssh me@mysystem ' ( touch /home/me/alarms/low24 ) '")que la creación de este archivo haga que la computadora maestra use espeaky anuncie el bajo voltaje. Ya me envía un correo electrónico, pero como mi sistema ya dice la hora en la parte superior de la hora, tiene todo el resto. askubuntu.com/questions/977613/…
SDsolar

19

Puedes usar watchen tu script

watch -n 0.1 ls <your_folder>

Monitorea su carpeta y le enumera todo lo que contiene cada 0.1 segundos

Retirarse

No es en tiempo real, por lo que si un archivo fue creado y eliminado en menos de 0.1 segundos, entonces esto no funcionaría, watchsolo admite un mínimo de 0.1 segundos.


¡Eso era exactamente lo que estaba tratando de recordar! ¡¡Muchas gracias!!
Joabe Lucena

9

Supongo que la carpeta de destino (la llamaré isemptysolo por conveniencia) está vacía y está esperando que se suelten uno o más archivos allí.

Puede usar el siguiente comando:

ls -1A isempty | wc -l

solo para verificar si la carpeta todavía está vacía, de hecho devolverá un 0 si no hay un archivo nuevo (por lo tanto, la isemptycarpeta todavía está vacía) o, por otro lado, devolverá un valor mayor que 0 (en realidad el número de archivos actualmente en la carpeta).

Dicho esto, una prueba tonta si / luego puede hacer el resto del trabajo:

if [ $(ls -1A isempty | wc -l) -gt 0 ] ; then do_something ; fi

Por supuesto, la do_somethingfunción tendrá que manipular los archivos dentro de la isemptycarpeta y luego eliminarlos de la carpeta misma después del procesamiento.

Agregar una línea como la siguiente en su crontab ejecutará la comprobación una vez por minuto y, do_somethingpor supuesto , activará la acción si la carpeta no está vacía:

* * * * *     if [ $(ls -1A isempty | wc -l) -gt 0 ] ; then do_something ; fi

Esta solución funciona para sistemas de archivos remotos montados. Los desarrolladores de inotify-tools están trabajando en fusibles (o estaban a mediados de 2014).
Rondo

3
Nunca deberías usarlo lspara hacer scripts. Use findo simplemente globbing en su lugar: mywiki.wooledge.org/ParsingLs
andsens

6

Si desea detectar nuevos archivos, luego procesarlos y al final eliminar los archivos procesados ​​puede usar systemd.path . Este método se basa en inotify. Hay una opción DirectoryNotEmpty, por lo que systemd puede ejecutar su script siempre que detecte cualquier archivo en el directorio. Debe recordar que funcionará solo si puede eliminar los archivos procesados ​​y el script deja el directorio vacío.

Primero prepare el archivo mymonitor.service

[Unit]
Description=Start the script

[Service]
Type=oneshot
ExecStart=/path/to/your/script

luego vaya a mymonitor.path para definir la ruta

[Unit]
Description= Triggers the service

[Path]
DirectoryNotEmpty=/path/to/monitor

[Install]
WantedBy=multi-user.target

Si el nombre del archivo .path es el mismo que el del servicio, no es necesario especificar el nombre del servicio en el archivo .path.

Se basa en el Monitoreo de Acceso a Archivos para Dummies


4

entr

Usar entres la nueva forma de hacer esto (es multiplataforma). Note entrno utiliza encuestas, lo que le da una gran ventaja sobre muchas de las alternativas.

Usos kqueue(2)o inotify(7)para evitar encuestas. entrfue escrito para hacer comentarios rápidos y pruebas automatizadas naturales y completamente normales.

En BSD usa pledge(2)

Puedes instalarlo con

apt-get install entr
dnf install entr

Puede rastrear un directorio para nuevas incorporaciones usando

while $(true); do
  # echo ./my_watch_dir | entr -dnr echo "Running trigger..."
  echo ./my_watch_dir | entr -dnr ##MY COMMAND##
done;

Opciones explicadas (de los documentos),

  • -d Rastree los directorios de archivos regulares proporcionados como entrada y salga si se agrega un nuevo archivo. Esta opción también permite que los directorios se especifiquen explícitamente. Archivos con nombres que comienzan con '.' son ignorados
  • -nEjecutar en modo no interactivo. En este modo, entr no intenta leer desde el TTY ni cambiar sus propiedades.
  • -r Vuelva a cargar un proceso hijo persistente. Al igual que con el modo de operación estándar, una utilidad que finaliza no se ejecuta nuevamente hasta que se procesa un sistema de archivos o un evento de teclado. SIGTERMse utiliza para finalizar la utilidad antes de que se reinicie. Se crea un grupo de procesos para evitar que los scripts de shell enmascaren las señales. entrespera a que salga la utilidad para garantizar que se hayan cerrado recursos como los sockets. El control del TTY no se transfiere al proceso hijo.

2

Bash no puede hacer esto fácilmente. Básicamente, tendría que obtener una lista de todos los archivos en la carpeta y obtener periódicamente una nueva lista y compararlos para ver qué ha cambiado.

Lo que estás buscando se llama inotify. Está integrado en el kernel de Linux y básicamente puedes sentarte allí esperando que algo suceda en ese momento, inotify regresa y dice 'hey, hay un nuevo archivo llamado foobar'

Para lograr lo que quieres, deberías cambiar a algo como perl y usar Linux :: Inotify2 (python probablemente también sea compatible con inotify, pero soy una persona perl).


0

Esto funciona en Cygwin y Linux. Algunas de las soluciones anteriores que escriben un archivo harán que el disco se dañe. Este scipt no tiene ese problema:

SIG=1
SIG0=$SIG
while [ $SIG != 0 ] ; do
 while [ $SIG = $SIG0 ] ; do
   SIG=`ls -1 | md5sum | cut -c1-32`
   sleep 10
 done
 SIG0=$SIG
 ls -lrt | tail -n 1
done

0

A continuación se muestra una versión abreviada de ejemplo en stackoverflow que he probado e incorporado a uno de mis proyectos que requiere la supervisión de directorios específicos.

Var_dir="${1:-/tmp}"
Var_diff_sleep="${2:-120}"
Var_diff_opts="--suppress-common-lines"
Func_parse_diff(){
    _added="$(grep -E '>' <<<"${@}")"
    if [ "${#_added}" != "0" ]; then
        mapfile -t _added_list <<<"${_added//> /}"
        _let _index=0
        until [ "${#_added_list[@]}" = "${_index}" ]; do
            _path_to_check="${Var_dir}/${_added_list[${_index}]}"
            if [ -f "${_path_to_check}" ]; then
                echo "# File: ${_path_to_check}"
            elif [ -d "${_path_to_check}" ]; then
                echo "# Directory: ${_path_to_check}"
            if [ -p "${_path_to_check}" ]; then
                echo "# Pipe: ${_path_to_check}"
            fi
            let _index++
        done
        unset _index
    fi
}
Func_watch_bulk_dir(){
    _current_listing=""
    while [ -d "${Var_dir}" ]; do
        _new_listing="$(ls "${Var_dir}")"
        _diff_listing="$(diff ${Var_dec_diff_opts} <(${Var_echo} "${_current_listing}") <(${Var_echo} "${_new_listing}"))"
        if [ "${_diff_listing}" != "0" ]; then
            Func_parse_diff "${_diff_listing}"
        fi
        _current_listing="${_new_listing}"
        sleep ${Var_diff_sleep}
    done
}

Aquí hay un enlace a un script que usa una versión modificada de arriba para descifrar automáticamente los archivos o directorios que se encuentran en su punto de montaje sshfs; El proyecto mencionado anteriormente.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.