joe di castrohttp://joedicastro.com2012-10-19T22:40:00+02:00Productividad & Linux: Ncdu2012-10-19T22:40:00+02:00joe di castrohttp://joedicastro.com/productividad-linux-ncdu.html<p>Esta es una pequeña aplicación que sigue la máxima de <a href="http://es.wikipedia.org/wiki/Douglas_McIlroy">Doug Mcllroy</a> que
acabaría resumiendo la filosofía Unix <em>"Escribe programas que hagan una sola
cosa y que la hagan bien"</em>. Y siguiendo la línea de los programas que engloban
esta serie de productividad sobre Linux, es un programa de consola que se maneja
íntegramente con el teclado, y donde además los atajos están inspirados en los
de <a href="http://www.vim.org/">Vim</a>.</p>
<h2 id="ncdu">Ncdu</h2>
<p><a href="http://dev.yorhel.nl/ncdu">Ncdu</a> es un acrónimo para "NCurses Disk Usage", es decir, "Uso de disco
con interfaz ncurses". Lo que pretendía <a href="https://github.com/yorhel">Yoran Heling</a>, su autor, cuando
creo <strong>ncdu</strong>, era obtener un analizador de uso de disco en consola que fuera lo
suficientemente ligero para poder emplearlo en un servidor remoto a través de
<a href="https://es.wikipedia.org/wiki/Ssh">ssh</a>. El gran acierto fue emplear la interfaz <a href="https://es.wikipedia.org/wiki/Ncurses">ncurses</a> para
poder navegar por los directorios con extrema facilidad al mismo tiempo que se
presentaba la información en un formato legible y amigable. El segundo y no
menos importante acierto, es emplear los conocidos atajos de teclado de Vim para
navegar entre los directorios.</p>
<p>El resultado es un analizador de ocupación de disco que es realmente útil,
agradable de utilizar y que es tan rápido y ligero que se puede emplear sin
interfaz gráfica y en maquinas remotas. Es una gran ayuda para los
administradores de sistemas, que hasta ahora nos teníamos que pelear con otras
herramientas bastante menos agradables. Es la típica herramienta que cuando la
utilizas por primera vez piensas ¿Por qué demonios no la habría encontrado
antes?</p>
<p style="text-align:center;"><img src="pictures/ncdu.png" width="700"
height="559" alt="ncdu" /></p>
<p>Como se puede ver la interfaz es muy limpia y carece de elementos superfluos que
nos distraigan de lo importante, un pecado que cometen algunas de las
aplicaciones gráficas más famosas, que cuentan con unos gráficos muy bonitos,
pero realmente no demasiado útiles. Es sencillisima de emplear una vez que
conoces los atajos y cuenta con ayuda integrada a la que se accede pulsando
<strong><code>?</code></strong>. Incluso para aquellos que no están acostumbrados a navegar empleando
las típicas teclas de vim (<strong>h</strong>, <strong>j</strong>, <strong>k</strong>, <strong>l</strong>), se pueden utilizar las
teclas de dirección.</p>
<h3 id="caracter+sticas">Características</h3>
<p>Un programa de este tipo no necesita una funcionalidad muy elevada, pero
destacare aquí algunas de las posibilidades que nos ofrece:</p>
<ul>
<li>Podemos ordenar los directorios/ficheros en función de su nombre o de su
tamaño y en orden ascendente o descendente </li>
<li>Se pueden eliminar directamente ficheros o directorios desde la aplicación,
aunque también podemos iniciarla en modo solo lectura para prevenir borrados
accidentales</li>
<li>Se muestra o no, el porcentaje de espacio ocupado de forma gráfica y/o numérica</li>
<li>El espacio empleado en disco puede hacer referencia al real o al aparente</li>
<li>Los directorios y ficheros ocultos pueden ser o no mostrados</li>
<li>Nos permite recalcular el espacio ocupado de un directorio concreto sin
necesidad de volver a recalcular todo </li>
<li>Tenemos la posibilidad de volcar la información a un archivo, que puede
también ser leído por el mismo programa</li>
<li>Es posible mostrar la información de un elemento individual en un cuadro
emergente</li>
</ul>
<p>La verdad es que lo único que hecho en falta en el programa es quizás un poco de
color para distinguir entre ficheros y directorios de forma más visual. Por lo
demás, es muy rápido calculando el espacio, no he hecho una comparación directa,
pero me parece ligeramente más rápido que Baobab.</p>
<h3 id="alternativas">Alternativas</h3>
<p>Existen varias alternativas decentes como aplicaciones gráficas, como los
conocidos <a href="http://www.marzocca.net/linux/baobab/">Baobab</a> (que viene con las herramientas de Gnome) o
<a href="http://www.jgoodies.com/freeware/jdiskreport/">JDiskReport</a>. Después existen múltiples alternativas tanto en modo
gráfico como en modo consola que, sinceramente, no puedo recomendar a nadie,
bien porque son más vistosos que prácticos o bien porque son directamente
insufribles. Existe una alternativa en modo texto que algunos prefieren a ncdu
que es <a href="http://gt5.sourceforge.net/">gt5</a>.</p>Monitorizar los cambios de tamaño en un directorio2011-05-17T21:52:00+02:00joe di castrohttp://joedicastro.com/monitorizar-los-cambios-de-tamano-en-un-directorio.html<p>Cuando administramos varias maquinas <abbr title="Linux, Unix, Solaris, BSD, etc">UN*X</abbr> nos puede interesar el tener
controlados los cambios de tamaño en algunos directorios determinados, para
poder observar pautas de comportamiento o ver cambios inesperados, para
solucionar los problemas cuando o antes de que se produzcan. Y aunque hay varias
formas de realizar esto, incluso con demonios que monitorizan los cambios en
tiempo real (incrond, inotify, dnotify, gamin, watch, ...), puede que una
solución más sencilla nos sea suficiente para directorios no críticos. Para
instalaciones no complejas nos puede servir, por ejemplo, para no tener que
lidiar con las <a href="http://en.wikipedia.org/wiki/Disk_quota">quotas de disco</a> (no siempre es una buena opción). O para
evitar que por ejemplo una mala configuración en la rotación de los logs de una
maquina acaben agotando el espacio disponible para la partición de sistema (caso
real que me he encontrado más de una vez). </p>
<p>Para poder monitorizar los cambios de tamaño de un directorio (y subdirectorios)
he creado un sencillo script <strong>Python</strong> que registra los cambios en la ruta que
le proporcionemos y luego envía un informe por correo al buzón del usuario
local. Los datos de los directorios los registra en un fichero oculto (su
nombre empieza por un <code>.</code>) binario de tipo <a href="http://docs.python.org/library/pickle.html#module-pickle">pickle</a> y el informe se guarda
a su vez en un archivo de texto con el mismo nombre que el script, pero
terminado en <code>.log</code>. </p>
<p>El informe que genera nos muestra por un lado tantos los nuevos directorios,
como los directorios que se han eliminado con la cifra del espacio en disco que
ocupan (o liberan). Por otro lado también nos informa de los directorios que han
cambiado de tamaño, mostrándonos en que porcentaje se han
incrementado/decrementado y la cantidad de espacio que ha variado. Aquí podemos
ver un ejemplo de uno de estos informes.</p>
<div class="codehilite"><pre>SCRIPT =========================================================================
dir_size_monitor (ver. 0.2)
http://joedicastro.com
Changes in size of directories for .. on yourmachine
================================================================================
START TIME =====================================================================
Tuesday 05/17/11, 21:10:48
================================================================================
NEW DIRECTORIES ________________________________________________________________
799.72 KiB ./src/test/bibendum
1.14 MiB ./src/test/condimentum
2.31 MiB ./src/test/laoreet
204.28 KiB ./src/test/risus
2.90 MiB ./src/test/torquent
DELETED DIRECTORIES ____________________________________________________________
383.79 KiB ./src/test/adipiscing
5.38 MiB ./src/test/consequat
847.72 KiB ./src/test/etiam
938.93 KiB ./src/test/maecenas
3.55 MiB ./src/test/tincidunt
2.33 MiB ./src/test/viverra
CHANGED DIRECTORIES ____________________________________________________________
34.79 % 55.5 MiB ./src
34.82 % 55.5 MiB ./src/test
-99.97 % 15.3 MiB ./src/test/odio
-99.97 % 15.6 MiB ./src/test/tellus
THRESHOLD VALUES _______________________________________________________________
The directories whose size differences are less than any of these values are ignored:
Percentage: 10 %
Size: 10.00 MiB
.. STATISTICS __________________________________________________________________
78 directories
215.04 MiB
END TIME =======================================================================
Tuesday 05/17/11, 21:10:48
================================================================================
</pre></div>
<p>Opcionalmente podemos establecer dos valores de umbral para que se ignoren todos
los cambios que estén por debajo de estas dos cifras. Para desactivarlos
simplemente hay que dejarlos a cero. Estas cifras se refieren por un lado al
porcentaje de diferencia mínimo que deseamos establecer para que se nos informe
y por otro a la cantidad mínima (expresada en bytes) de espacio en disco que se
ha incrementado/decrementado en ese directorio. Pueden funcionar los dos a la
vez, por lo que se han de cumplir las dos condiciones, o solamente uno dejando
el otro a cero.</p>
<p>Si programamos este script para que se ejecute cada cierto tiempo, podemos tener
una idea aproximada de los cambios producidos en el. Y digo aproximada porque
este nos muestra únicamente los cambios registrados entre dos <em>instantáneas</em>
tomadas, una en la ejecución anterior y otra en la ejecución actual. Y por lo
tanto no esperemos obtener la relación de todos los cambios producidas entre
ellas. Para conocer algo a ese nivel de detalle es mejor emplear uno de los
servicios en tiempo real que mencionaba al principio. Dicho esto, es evidente
que en la primera ejecución no tiene sentido informar de nada y de hecho hasta
la segunda ejecución no empezara a generar informes.</p>
<p>Este es el contenido de <a href="https://github.com/joedicastro/python-recipes/blob/master/dir_size_monitor.py">dir_size_monitor.py</a> es el siguiente:</p>
<div class="codehilite"><pre><span class="c">#!/usr/bin/env python</span>
<span class="c"># -*- coding: utf8 -*-</span>
<span class="sd">"""</span>
<span class="sd"> dir_size_monitor.py: Monitors changes in the size of dirs for a given path</span>
<span class="sd">"""</span>
<span class="c">#===============================================================================</span>
<span class="c"># This Script monitors the changes in disk size for the directories included in</span>
<span class="c"># a given path. It reports what directories are new or deleted. Also reports the</span>
<span class="c"># directories in which their size increases or decreases above threshold values.</span>
<span class="c"># These threshold values refer to the amount in difference of size of the </span>
<span class="c"># directory or/and the percentage difference. These values can be overrided by </span>
<span class="c"># setting them to zero.</span>
<span class="c">#</span>
<span class="c"># The final report is sended via email to the local user. This script is </span>
<span class="c"># intended to run periodically (e.g. via cron) </span>
<span class="c">#===============================================================================</span>
<span class="c">#===============================================================================</span>
<span class="c"># Copyright 2011 joe di castro <joe@joedicastro.com></span>
<span class="c">#</span>
<span class="c"># This program is free software: you can redistribute it and/or modify</span>
<span class="c"># it under the terms of the GNU General Public License as published by</span>
<span class="c"># the Free Software Foundation, either version 3 of the License, or</span>
<span class="c"># (at your option) any later version.</span>
<span class="c">#</span>
<span class="c"># This program is distributed in the hope that it will be useful,</span>
<span class="c"># but WITHOUT ANY WARRANTY; without even the implied warranty of</span>
<span class="c"># MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the</span>
<span class="c"># GNU General Public License for more details.</span>
<span class="c">#</span>
<span class="c"># You should have received a copy of the GNU General Public License</span>
<span class="c"># along with this program. If not, see <http://www.gnu.org/licenses/>.</span>
<span class="c">#===============================================================================</span>
<span class="n">__author__</span> <span class="o">=</span> <span class="s">"joe di castro <joe@joedicastro.com>"</span>
<span class="n">__license__</span> <span class="o">=</span> <span class="s">"GNU General Public License version 3"</span>
<span class="n">__date__</span> <span class="o">=</span> <span class="s">"17/05/2011"</span>
<span class="n">__version__</span> <span class="o">=</span> <span class="s">"0.2"</span>
<span class="k">try</span><span class="p">:</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">platform</span>
<span class="kn">import</span> <span class="nn">pickle</span>
<span class="kn">import</span> <span class="nn">logger</span>
<span class="kn">from</span> <span class="nn">get_size</span> <span class="kn">import</span> <span class="n">best_unit_size</span><span class="p">,</span> <span class="n">get_size_fast</span>
<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
<span class="c"># Checks the installation of the necessary python modules </span>
<span class="k">print</span><span class="p">((</span><span class="n">os</span><span class="o">.</span><span class="n">linesep</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="s">"An error found importing one module:"</span><span class="p">,</span>
<span class="nb">str</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">exc_info</span><span class="p">()[</span><span class="mi">1</span><span class="p">]),</span> <span class="s">"You need to install it"</span><span class="p">,</span> <span class="s">"Stopping..."</span><span class="p">]))</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">list4log</span><span class="p">(</span><span class="n">dirs_size_dict</span><span class="p">,</span> <span class="n">wpath</span><span class="p">,</span> <span class="n">dirs</span><span class="p">):</span>
<span class="sd">"""Create a list of new or deleted directories for the log."""</span>
<span class="n">llst</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">ldir</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">dirs</span><span class="p">):</span>
<span class="n">dsz</span> <span class="o">=</span> <span class="n">best_unit_size</span><span class="p">(</span><span class="n">dirs_size_dict</span><span class="p">[</span><span class="n">ldir</span><span class="p">])</span>
<span class="n">llst</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">" {0:8.2f} {1} ./{2}"</span><span class="o">.</span>
<span class="n">format</span><span class="p">(</span><span class="n">dsz</span><span class="p">[</span><span class="s">'s'</span><span class="p">],</span> <span class="n">dsz</span><span class="p">[</span><span class="s">'u'</span><span class="p">],</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">relpath</span><span class="p">(</span><span class="n">ldir</span><span class="p">,</span> <span class="n">wpath</span><span class="p">)))</span>
<span class="k">return</span> <span class="n">llst</span>
<span class="k">def</span> <span class="nf">diff4log</span><span class="p">(</span><span class="n">before</span><span class="p">,</span> <span class="n">current</span><span class="p">,</span> <span class="n">wpath</span><span class="p">,</span> <span class="n">dirs</span><span class="p">,</span> <span class="n">threshold_pct</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">threshold_sz</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
<span class="sd">"""Create a list of the directories that had size changes for the log."""</span>
<span class="n">llst</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">ddir</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">dirs</span><span class="p">):</span>
<span class="n">pct</span> <span class="o">=</span> <span class="p">(((</span><span class="n">current</span><span class="p">[</span><span class="n">ddir</span><span class="p">]</span> <span class="o">-</span> <span class="nb">float</span><span class="p">(</span><span class="n">before</span><span class="p">[</span><span class="n">ddir</span><span class="p">]))</span> <span class="o">/</span> <span class="n">before</span><span class="p">[</span><span class="n">ddir</span><span class="p">])</span> <span class="o">*</span> <span class="mf">100.0</span><span class="p">)</span>
<span class="n">diff</span> <span class="o">=</span> <span class="n">current</span><span class="p">[</span><span class="n">ddir</span><span class="p">]</span> <span class="o">-</span> <span class="n">before</span><span class="p">[</span><span class="n">ddir</span><span class="p">]</span>
<span class="k">if</span> <span class="nb">abs</span><span class="p">(</span><span class="n">pct</span><span class="p">)</span> <span class="o">>=</span> <span class="n">threshold_pct</span> <span class="ow">and</span> <span class="nb">abs</span><span class="p">(</span><span class="n">diff</span><span class="p">)</span> <span class="o">></span> <span class="n">threshold_sz</span><span class="p">:</span>
<span class="n">dsz</span> <span class="o">=</span> <span class="n">best_unit_size</span><span class="p">(</span><span class="n">diff</span><span class="p">)</span>
<span class="n">llst</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">" {0:8.2f} % {1:8.1f} {2} ./{3}"</span><span class="o">.</span>
<span class="n">format</span><span class="p">(</span><span class="n">pct</span><span class="p">,</span> <span class="n">dsz</span><span class="p">[</span><span class="s">'s'</span><span class="p">],</span> <span class="n">dsz</span><span class="p">[</span><span class="s">'u'</span><span class="p">],</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">relpath</span><span class="p">(</span><span class="n">ddir</span><span class="p">,</span>
<span class="n">wpath</span><span class="p">)))</span>
<span class="k">return</span> <span class="n">llst</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">first_exec</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
<span class="sd">"""Main section"""</span>
<span class="c"># The path to monitor changes in directories dir_size</span>
<span class="n">mon_pth</span> <span class="o">=</span> <span class="s">"/your/path/to/monitor"</span>
<span class="c"># Ignore all directories that are below these percentage or absolute value </span>
<span class="c"># of size difference. There are optional, set to zero to override them.</span>
<span class="n">thld_pct</span> <span class="o">=</span> <span class="mi">20</span> <span class="c"># In percentage of difference in size for a directory</span>
<span class="n">thld_sz</span> <span class="o">=</span> <span class="mf">10.486E6</span> <span class="c"># In bytes of absolute value of directory size difference</span>
<span class="c"># Prepare the log</span>
<span class="n">log</span> <span class="o">=</span> <span class="n">logger</span><span class="o">.</span><span class="n">Logger</span><span class="p">()</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"http://joedicastro.com"</span>
<span class="n">head</span> <span class="o">=</span> <span class="p">(</span><span class="s">"Changes in size of directories for {0} on {1}"</span><span class="o">.</span>
<span class="n">format</span><span class="p">(</span><span class="n">mon_pth</span><span class="p">,</span> <span class="n">platform</span><span class="o">.</span><span class="n">node</span><span class="p">()))</span>
<span class="n">log</span><span class="o">.</span><span class="n">header</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">head</span><span class="p">)</span>
<span class="n">log</span><span class="o">.</span><span class="n">time</span><span class="p">(</span><span class="s">"START TIME"</span><span class="p">)</span>
<span class="c"># Load the last dictionary of directories/sizes if exists</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">'.dir_sizes.pkl'</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">input_file</span><span class="p">:</span>
<span class="n">bfr_dir</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">input_file</span><span class="p">)</span>
<span class="k">except</span> <span class="p">(</span><span class="ne">EOFError</span><span class="p">,</span> <span class="ne">IOError</span><span class="p">,</span> <span class="n">pickle</span><span class="o">.</span><span class="n">PickleError</span><span class="p">):</span>
<span class="n">bfr_dir</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">first_exec</span> <span class="o">=</span> <span class="bp">True</span>
<span class="c"># Get the current dictionary of directories/sizes</span>
<span class="n">crr_dir</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">path</span><span class="p">,</span> <span class="n">dirs</span><span class="p">,</span> <span class="n">files</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">mon_pth</span><span class="p">):</span>
<span class="k">for</span> <span class="n">directory</span> <span class="ow">in</span> <span class="n">dirs</span><span class="p">:</span>
<span class="n">dir_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span>
<span class="n">dir_size</span> <span class="o">=</span> <span class="n">get_size_fast</span><span class="p">(</span><span class="n">dir_path</span><span class="p">)</span>
<span class="n">crr_dir</span><span class="p">[</span><span class="n">dir_path</span><span class="p">]</span> <span class="o">=</span> <span class="n">dir_size</span>
<span class="c"># First, Save the current dirs/sizes</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">".dir_sizes.pkl"</span><span class="p">,</span> <span class="s">"wb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">output_file</span><span class="p">:</span>
<span class="n">pickle</span><span class="o">.</span><span class="n">dump</span><span class="p">(</span><span class="n">crr_dir</span><span class="p">,</span> <span class="n">output_file</span><span class="p">)</span>
<span class="c"># Create the list depending the status of directories</span>
<span class="n">deleted</span> <span class="o">=</span> <span class="p">[</span><span class="n">d</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">bfr_dir</span> <span class="k">if</span> <span class="n">d</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">crr_dir</span><span class="p">]</span>
<span class="n">added</span> <span class="o">=</span> <span class="p">[</span><span class="n">d</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">crr_dir</span> <span class="k">if</span> <span class="n">d</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">bfr_dir</span><span class="p">]</span>
<span class="n">changed</span> <span class="o">=</span> <span class="p">[</span><span class="n">d</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">crr_dir</span> <span class="k">if</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">bfr_dir</span> <span class="k">if</span> <span class="n">crr_dir</span><span class="p">[</span><span class="n">d</span><span class="p">]</span> <span class="o">!=</span> <span class="n">bfr_dir</span><span class="p">[</span><span class="n">d</span><span class="p">]]</span>
<span class="n">log</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">"Deleted directories"</span><span class="p">,</span> <span class="n">list4log</span><span class="p">(</span><span class="n">bfr_dir</span><span class="p">,</span> <span class="n">mon_pth</span><span class="p">,</span> <span class="n">deleted</span><span class="p">))</span>
<span class="n">log</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">"New directories"</span><span class="p">,</span> <span class="n">list4log</span><span class="p">(</span><span class="n">crr_dir</span><span class="p">,</span> <span class="n">mon_pth</span><span class="p">,</span> <span class="n">added</span><span class="p">))</span>
<span class="n">log</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">"Changed directories"</span><span class="p">,</span> <span class="n">diff4log</span><span class="p">(</span><span class="n">bfr_dir</span><span class="p">,</span> <span class="n">crr_dir</span><span class="p">,</span> <span class="n">mon_pth</span><span class="p">,</span> <span class="n">changed</span><span class="p">,</span>
<span class="n">thld_pct</span><span class="p">,</span> <span class="n">thld_sz</span><span class="p">))</span>
<span class="c"># If thresholds are nonzero, then report the values </span>
<span class="k">if</span> <span class="n">thld_pct</span> <span class="ow">or</span> <span class="n">thld_sz</span><span class="p">:</span>
<span class="n">tsz</span> <span class="o">=</span> <span class="n">best_unit_size</span><span class="p">(</span><span class="n">thld_sz</span><span class="p">)</span>
<span class="n">log</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">"Threshold Values"</span><span class="p">,</span>
<span class="p">[</span><span class="s">"The directories whose size differences are less than any of "</span>
<span class="s">"these values are ignored:"</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span>
<span class="s">"Percentage: {0:6} %"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">thld_pct</span><span class="p">),</span>
<span class="s">"Size: {0:6.2f} {1}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">tsz</span><span class="p">[</span><span class="s">'s'</span><span class="p">],</span> <span class="n">tsz</span><span class="p">[</span><span class="s">'u'</span><span class="p">])])</span>
<span class="c"># Show some statistics for the analyzed path</span>
<span class="n">mon_pth_sz</span> <span class="o">=</span> <span class="n">best_unit_size</span><span class="p">(</span><span class="n">get_size_fast</span><span class="p">(</span><span class="n">mon_pth</span><span class="p">))</span>
<span class="n">log</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">"{0} Statistics"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">mon_pth</span><span class="p">),</span>
<span class="p">[</span><span class="s">"{0:8} directories"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">crr_dir</span><span class="p">)),</span>
<span class="s">"{0:8.2f} {1}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">mon_pth_sz</span><span class="p">[</span><span class="s">'s'</span><span class="p">],</span> <span class="n">mon_pth_sz</span><span class="p">[</span><span class="s">'u'</span><span class="p">])])</span>
<span class="n">log</span><span class="o">.</span><span class="n">time</span><span class="p">(</span><span class="s">"END TIME"</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">first_exec</span><span class="p">:</span>
<span class="n">log</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s">"Changes in size of directories"</span><span class="p">)</span>
<span class="n">log</span><span class="o">.</span><span class="n">write</span><span class="p">()</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</pre></div>
<p>Este script necesita los módulos <a href="http://joedicastro.com/conocer_el_tamano_de_un_directorio_con_python">get_size</a> y <a href="http://joedicastro.com/logger_informes_legibles_para_tus_scripts_python">logger</a> para poder
funcionar. Solo es necesario descargar los archivos y guardarlos en el mismo
directorio donde se aloje este script. La versión más actualizada de este script
se puede encontrar en el repositorio <em>Python Recipes</em> alojado en <a href="http://github.com/joedicastro/python-recipes">github</a>.</p>Conocer el tamaño de un directorio con Python2011-05-16T21:05:00+02:00joe di castrohttp://joedicastro.com/conocer-el-tamano-de-un-directorio-con-python.html<p>Aunque conocer el tamaño de un directorio en sistemas como Linux es algo
trivial, solo es necesario emplear el comando <code>du</code>, si queremos hacer lo mismo
con <strong>Python</strong> -sin hacer uso de este comando- la cosa ya no es tan sencilla.
Sobre todo si lo que queremos es una solución que nos devuelva tanto el tamaño
de un fichero como el de un directorio. Cuando me encontré con esta necesidad lo
primero que hice fue buscar en Internet para conocer alguna solución previa
(reinventar la rueda no siempre es lo mejor) y me encontré con esto:</p>
<div class="codehilite"><pre><span class="k">def</span> <span class="nf">get_dir_size</span><span class="p">(</span><span class="n">the_path</span><span class="p">):</span>
<span class="sd">"""Get size of a directory tree in bytes."""</span>
<span class="n">path_size</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">path</span><span class="p">,</span> <span class="n">dirs</span><span class="p">,</span> <span class="n">files</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">the_path</span><span class="p">):</span>
<span class="k">for</span> <span class="n">fil</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span>
<span class="n">filename</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">fil</span><span class="p">)</span>
<span class="n">path_size</span> <span class="o">+=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">getsize</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
<span class="k">return</span> <span class="n">path_size</span>
</pre></div>
<p>Esta solución nos daría el tamaño en bytes de un directorio. Pero esta solución,
que encontré en varios sitios, presentaba dos problemas:</p>
<ul>
<li>
<p><strong>No da un tamaño exacto</strong>. Esto se debe a que no tiene en cuenta las carpetas
y ficheros ocultos (los que empiezan con un <code>.</code> en Linux) y los ficheros
especiales <code>..</code> (que apuntan al directorio superior). Además tampoco tiene en
cuenta los enlaces simbólicos. Por está razón la salida de esta función no
coincide con el espacio que nos reporta el comando <abbr title="Linux, Unix, Solaris, BSD, etc">UN*X</abbr> <code>du -bs</code></p>
</li>
<li>
<p><strong>No funciona para un solo fichero</strong>. Solo trabaja cuando lo ejecutamos sobre
un directorio, al hacerlo sobre un solo fichero nos dará como resultado siempre 0.</p>
</li>
</ul>
<p>Teniendo en cuenta este punto de partida, elaboré una función que solucionara
estos dos problemas y que devolviera el tamaño exacto de un directorio o
fichero. Esta es la <strong>función que nos da el resultado correcto</strong>:</p>
<div class="codehilite"><pre><span class="k">def</span> <span class="nf">get_size</span><span class="p">(</span><span class="n">the_path</span><span class="p">):</span>
<span class="sd">"""Get size of a directory tree or a file in bytes."""</span>
<span class="n">path_size</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">path</span><span class="p">,</span> <span class="n">directories</span><span class="p">,</span> <span class="n">files</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">the_path</span><span class="p">):</span>
<span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span>
<span class="n">path_size</span> <span class="o">+=</span> <span class="n">os</span><span class="o">.</span><span class="n">lstat</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">filename</span><span class="p">))</span><span class="o">.</span><span class="n">st_size</span>
<span class="k">for</span> <span class="n">directory</span> <span class="ow">in</span> <span class="n">directories</span><span class="p">:</span>
<span class="n">path_size</span> <span class="o">+=</span> <span class="n">os</span><span class="o">.</span><span class="n">lstat</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">directory</span><span class="p">))</span><span class="o">.</span><span class="n">st_size</span>
<span class="n">path_size</span> <span class="o">+=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">getsize</span><span class="p">(</span><span class="n">the_path</span><span class="p">)</span>
<span class="k">return</span> <span class="n">path_size</span>
</pre></div>
<p>El resultado de esta función es el mismo que el que nos devuelve el comando
Linux <code>du -bs</code>. Además tiene en cuenta los enlaces simbólicos y no los sigue.
Luego buscando una <strong>solución ligeramente más rápida</strong> (aunque menos elegante y
<em>pythonica</em>) y que siguiera dando resultados precisos, cree una variante basada
en el empleo de generadores. </p>
<div class="codehilite"><pre><span class="k">def</span> <span class="nf">get_size_fast</span><span class="p">(</span><span class="n">the_path</span><span class="p">):</span>
<span class="sd">"""Get size of a directory tree or a file in bytes."""</span>
<span class="k">def</span> <span class="nf">get_sizes</span><span class="p">(</span><span class="n">the_path</span><span class="p">):</span>
<span class="sd">"""Make a generator of individual file & directory sizes."""</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">islink</span><span class="p">(</span><span class="n">the_path</span><span class="p">):</span>
<span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isdir</span><span class="p">(</span><span class="n">the_path</span><span class="p">):</span>
<span class="k">for</span> <span class="n">file_or_dir</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">the_path</span><span class="p">):</span>
<span class="n">path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">the_path</span><span class="p">,</span> <span class="n">file_or_dir</span><span class="p">)</span>
<span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
<span class="k">yield</span> <span class="n">os</span><span class="o">.</span><span class="n">lstat</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="o">.</span><span class="n">st_size</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">for</span> <span class="n">size</span> <span class="ow">in</span> <span class="n">get_sizes</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
<span class="k">yield</span> <span class="n">size</span>
<span class="k">yield</span> <span class="n">os</span><span class="o">.</span><span class="n">lstat</span><span class="p">(</span><span class="n">the_path</span><span class="p">)</span><span class="o">.</span><span class="n">st_size</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">yield</span> <span class="n">os</span><span class="o">.</span><span class="n">lstat</span><span class="p">(</span><span class="n">the_path</span><span class="p">)</span><span class="o">.</span><span class="n">st_size</span>
<span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">get_sizes</span><span class="p">(</span><span class="n">the_path</span><span class="p">))</span>
</pre></div>
<h2 id="obtener_el_tama+o_del_directorio_en_la_mejor_unidad_posible">Obtener el tamaño del directorio en la mejor unidad posible</h2>
<p>Estas funciones proporcionan el resultado que deseamos, pero lo entregan en una
unidad difícilmente legible, en bytes. ¿Que ocurre si queremos verlo en
<a href="http://es.wikipedia.org/wiki/Prefijo_binario">Mebibytes, GibiBytes</a>, ... y que además sea siempre la más adecuada para una
mejor visualización? Para responder a esta pregunta desarrolle una función que
nos hace precisamente esto, tomar un tamaño en bytes y devolvernos el valor
correcto en la <a href="http://physics.nist.gov/cuu/Units/binary.html">unidad binaria IEC</a> más adecuada:</p>
<div class="codehilite"><pre><span class="k">def</span> <span class="nf">best_unit_size</span><span class="p">(</span><span class="n">bytes_size</span><span class="p">):</span>
<span class="sd">"""Get a size in bytes & convert it to the best IEC prefix for readability.</span>
<span class="sd"> Return a dictionary with three pair of keys/values:</span>
<span class="sd"> "s" -- (float) Size of path converted to the best unit for easy read</span>
<span class="sd"> "u" -- (str) The prefix (IEC) for s (from bytes(2^0) to YiB(2^80))</span>
<span class="sd"> "b" -- (int / long) The original size in bytes</span>
<span class="sd"> """</span>
<span class="k">for</span> <span class="n">exp</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">90</span> <span class="p">,</span> <span class="mi">10</span><span class="p">):</span>
<span class="n">bu_size</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="n">bytes_size</span><span class="p">)</span> <span class="o">/</span> <span class="nb">pow</span><span class="p">(</span><span class="mf">2.0</span><span class="p">,</span> <span class="n">exp</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">int</span><span class="p">(</span><span class="n">bu_size</span><span class="p">)</span> <span class="o"><</span> <span class="mi">2</span> <span class="o">**</span> <span class="mi">10</span><span class="p">:</span>
<span class="n">unit</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">:</span><span class="s">"bytes"</span><span class="p">,</span> <span class="mi">10</span><span class="p">:</span><span class="s">"KiB"</span><span class="p">,</span> <span class="mi">20</span><span class="p">:</span><span class="s">"MiB"</span><span class="p">,</span> <span class="mi">30</span><span class="p">:</span><span class="s">"GiB"</span><span class="p">,</span> <span class="mi">40</span><span class="p">:</span><span class="s">"TiB"</span><span class="p">,</span> <span class="mi">50</span><span class="p">:</span><span class="s">"PiB"</span><span class="p">,</span>
<span class="mi">60</span><span class="p">:</span><span class="s">"EiB"</span><span class="p">,</span> <span class="mi">70</span><span class="p">:</span><span class="s">"ZiB"</span><span class="p">,</span> <span class="mi">80</span><span class="p">:</span><span class="s">"YiB"</span><span class="p">}[</span><span class="n">exp</span><span class="p">]</span>
<span class="k">break</span>
<span class="k">return</span> <span class="p">{</span><span class="s">"s"</span><span class="p">:</span><span class="n">bu_size</span><span class="p">,</span> <span class="s">"u"</span><span class="p">:</span><span class="n">unit</span><span class="p">,</span> <span class="s">"b"</span><span class="p">:</span><span class="n">bytes_size</span><span class="p">}</span>
</pre></div>
<p>Esta función nos devuelve un diccionario con tres claves:</p>
<ul>
<li><code>'s'</code>: Es el tamaño convertido a la mejor unidad IEC posible en términos de
legibilidad.</li>
<li><code>'u'</code>: Es el prefijo IEC para el tamaño anterior.</li>
<li><code>'b'</code>: Es el tamaño original en bytes.</li>
</ul>
<p>Para entenderla, lo mejor es mostrar algunos ejemplos:</p>
<div class="codehilite"><pre><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">get_size</span>
<span class="gp">>>> </span><span class="n">size</span> <span class="o">=</span> <span class="n">get_size</span><span class="o">.</span><span class="n">best_unit_size</span><span class="p">(</span><span class="mi">38467206502</span><span class="p">)</span>
<span class="gp">>>> </span><span class="s">"{0:.2f} {1}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">size</span><span class="p">[</span><span class="s">'s'</span><span class="p">],</span> <span class="n">size</span><span class="p">[</span><span class="s">'u'</span><span class="p">])</span>
<span class="go">'35.83 GiB'</span>
<span class="gp">>>> </span><span class="n">size</span> <span class="o">=</span> <span class="n">get_size</span><span class="o">.</span><span class="n">best_unit_size</span><span class="p">(</span><span class="mi">45332</span><span class="p">)</span>
<span class="gp">>>> </span><span class="s">"{0:.2f} {1}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">size</span><span class="p">[</span><span class="s">'s'</span><span class="p">],</span> <span class="n">size</span><span class="p">[</span><span class="s">'u'</span><span class="p">])</span>
<span class="go">'44.27 KiB'</span>
<span class="gp">>>> </span><span class="n">size</span> <span class="o">=</span> <span class="n">get_size</span><span class="o">.</span><span class="n">best_unit_size</span><span class="p">(</span><span class="mi">9878323</span><span class="p">)</span>
<span class="gp">>>> </span><span class="s">"{0:.2f} {1} es igual a {2} bytes"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">size</span><span class="p">[</span><span class="s">'s'</span><span class="p">],</span> <span class="n">size</span><span class="p">[</span><span class="s">'u'</span><span class="p">],</span> <span class="n">size</span><span class="p">[</span><span class="s">'b'</span><span class="p">])</span>
<span class="go">'9.42 MiB es igual a 9878323 bytes'</span>
</pre></div>
<p>Y evidentemente, combinar las dos funciones en una, nos evita tener que pasar
las dos a un mismo directorio/fichero. </p>
<div class="codehilite"><pre><span class="k">def</span> <span class="nf">get_unit_size</span><span class="p">(</span><span class="n">the_path</span><span class="p">):</span>
<span class="sd">"""Calculate size of a directory/file & convert it for the best IEC prefix.</span>
<span class="sd"> Return a dictionary with three pair of keys/values:</span>
<span class="sd"> "s" -- (float) Size of path converted to the best unit for easy read</span>
<span class="sd"> "u" -- (str) The prefix (IEC) for s (from bytes(2^0) to YiB(2^80))</span>
<span class="sd"> "b" -- (int / long) The original size in bytes</span>
<span class="sd"> """</span>
<span class="n">bytes_size</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">path</span><span class="p">,</span> <span class="n">directories</span><span class="p">,</span> <span class="n">files</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">the_path</span><span class="p">):</span>
<span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span>
<span class="n">bytes_size</span> <span class="o">+=</span> <span class="n">os</span><span class="o">.</span><span class="n">lstat</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">filename</span><span class="p">))</span><span class="o">.</span><span class="n">st_size</span>
<span class="k">for</span> <span class="n">directory</span> <span class="ow">in</span> <span class="n">directories</span><span class="p">:</span>
<span class="n">bytes_size</span> <span class="o">+=</span> <span class="n">os</span><span class="o">.</span><span class="n">lstat</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">directory</span><span class="p">))</span><span class="o">.</span><span class="n">st_size</span>
<span class="n">bytes_size</span> <span class="o">+=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">getsize</span><span class="p">(</span><span class="n">the_path</span><span class="p">)</span>
<span class="k">for</span> <span class="n">exp</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">90</span> <span class="p">,</span> <span class="mi">10</span><span class="p">):</span>
<span class="n">bu_size</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="n">bytes_size</span><span class="p">)</span> <span class="o">/</span> <span class="nb">pow</span><span class="p">(</span><span class="mf">2.0</span><span class="p">,</span> <span class="n">exp</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">int</span><span class="p">(</span><span class="n">bu_size</span><span class="p">)</span> <span class="o"><</span> <span class="mi">2</span> <span class="o">**</span> <span class="mi">10</span><span class="p">:</span>
<span class="n">unit</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">:</span><span class="s">"bytes"</span><span class="p">,</span> <span class="mi">10</span><span class="p">:</span><span class="s">"KiB"</span><span class="p">,</span> <span class="mi">20</span><span class="p">:</span><span class="s">"MiB"</span><span class="p">,</span> <span class="mi">30</span><span class="p">:</span><span class="s">"GiB"</span><span class="p">,</span> <span class="mi">40</span><span class="p">:</span><span class="s">"TiB"</span><span class="p">,</span> <span class="mi">50</span><span class="p">:</span><span class="s">"PiB"</span><span class="p">,</span>
<span class="mi">60</span><span class="p">:</span><span class="s">"EiB"</span><span class="p">,</span> <span class="mi">70</span><span class="p">:</span><span class="s">"ZiB"</span><span class="p">,</span> <span class="mi">80</span><span class="p">:</span><span class="s">"YiB"</span><span class="p">}[</span><span class="n">exp</span><span class="p">]</span>
<span class="k">break</span>
<span class="k">return</span> <span class="p">{</span><span class="s">"s"</span><span class="p">:</span><span class="n">bu_size</span><span class="p">,</span> <span class="s">"u"</span><span class="p">:</span><span class="n">unit</span><span class="p">,</span> <span class="s">"b"</span><span class="p">:</span><span class="n">bytes_size</span><span class="p">}</span>
</pre></div>
<p>Que nos devuelve un diccionario similar al anterior, lo que nos proporciona la
posibilidad de disponer tanto del tamaño en bytes como en la mejor unidad IEC
posible con una única función. </p>
<p>Todas estas funciones con ejemplos (y además una clase que hace uso de ellas),
se pueden encontrar en el fichero <code>get_size.py</code> en mi repositorio
<em>Python Recipes</em> que se encuentra alojado en <a href="http://github.com/joedicastro/python-recipes">github</a>. Si se ejecuta el
fichero como un script puede verse una comparativa de las diversas funciones en
rendimiento y precisión con respecto al comando <code>du -bs</code></p>