joe di castrohttp://joedicastro.comTue, 26 Apr 2011 23:17:00 +0200Mover todos los archivos del mismo tipo de un arbol de directorios a la vezhttp://joedicastro.com/mover-todos-los-archivos-del-mismo-tipo-de-un-arbol-de-directorios-a-la-vez.html<p>A veces tenemos la necesidad de <strong>mover (o copiar o borrar)</strong> varios ficheros del mismo tipo a la vez, pero estos no "cuelgan" de un solo directorio, si no que se encuentran repartidos dentro una jerarquía de subdirectorios. En este caso los típicos comandos para mover/copiar/borrar archivos no nos sirven, ni siquiera los útiles comodines nos resuelven el problema. ¿Como lo hacemos entonces?</p> <p>Por ejemplo, si tenemos una estructura de directorios como esta:</p> <div class="codehilite"><pre>raiz ├── dir_1 │ ├── imagen_1.jpg │ ├── pdf_1.pdf │ ├── texto_1.txt │ └── texto_2.txt ├── dir_2 │ ├── imagen_2.jpg │ ├── imagen_3.gif │ └── texto_3.txt ├── dir_3 │ ├── doc_1.doc │ ├── pdf_2.pdf │ ├── pdf_3.pdf │ ├── pdf_4.pdf │ ├── texto_4.txt │ └── texto_5.txt ├── dir_4 │ ├── subdir_1 │ │ └── imagen_4.png │ ├── subdir_2 │ │ ├── pdf_5.pdf │ │ └── texto_7.txt │ ├── doc_2.doc │ └── texto_6.txt └── dir_5 </pre></div> <p>Y queremos mover todos los ficheros <code>.txt</code> al <code>dir_5</code> que tenemos vacío, ¿Como lo hacemos? Bueno, en el caso de <strong>Linux</strong> (y UNIX) podemos hacerlo en una sola operación gracias al siempre útil y versátil <code>find</code> con el siguiente comando (ejecutado desde el directorio raíz):</p> <div class="codehilite"><pre>find . -type f -name *.txt -exec mv <span class="o">{}</span> ./dir_5 <span class="se">\;</span> </pre></div> <p>Este comando nos encontraría todos los archivos con la extensión <code>.txt</code> dentro de todos los directorios y subdirectorios y los iría moviendo uno a uno al <code>dir_5</code>. Es un comando rápido y efectivo. Genéricamente, el comando sería así:</p> <div class="codehilite"><pre>find directorio_origen -type f -name *.EXT -exec mv <span class="o">{}</span> ./directorio_destino <span class="se">\;</span> </pre></div> <p>donde <code>EXT</code> sería la extensión del tipo de archivo que queremos mover. Se puede emplear esta variante para copiar los archivos en vez de moverlos:</p> <div class="codehilite"><pre>find directorio_origen -type f -name *.EXT -exec cp <span class="o">{}</span> ./directorio_destino <span class="se">\;</span> </pre></div> <p>Y esta otra para eliminarlos (¡emplear con cuidado!):</p> <div class="codehilite"><pre>find directorio_origen -type f -name *.EXT -exec rm -f <span class="o">{}</span> <span class="se">\;</span> </pre></div> <p>Partiendo de la idea de hacer algo equivalente en <strong>Python</strong>, he creado un script que hace algo similar a esto, sin la necesidad de teclear todo el comando (y con el riesgo a equivocarse y liarla parda) y que también sirve para que los no trabajen habitualmente con la consola no necesiten recordar (o anotar) esos comandos.</p> <p>La parte principal del script <strong>move_by_ext.py</strong> es la siguiente:</p> <div class="codehilite"><pre><span class="k">def</span> <span class="nf">find_and_process</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">count</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">log</span><span class="o">=</span><span class="s">&quot;&quot;</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;Find the files by file extension and process (move/copy/remove) them.&quot;&quot;&quot;</span> <span class="k">def</span> <span class="nf">process</span><span class="p">(</span><span class="n">the_path</span><span class="p">,</span> <span class="n">the_file</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;Process each file.&quot;&quot;&quot;</span> <span class="n">processed</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">src_file</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">the_file</span><span class="p">)</span> <span class="n">dst_file</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">args</span><span class="o">.</span><span class="n">dst</span><span class="p">,</span> <span class="n">the_file</span><span class="p">)</span> <span class="k">if</span> <span class="n">args</span><span class="o">.</span><span class="n">rm</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">src_file</span><span class="p">)</span> <span class="n">processed</span> <span class="o">=</span> <span class="mi">1</span> <span class="k">else</span><span class="p">:</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">exists</span><span class="p">(</span><span class="n">dst_file</span><span class="p">):</span> <span class="c"># not replace if already exists </span> <span class="k">if</span> <span class="n">args</span><span class="o">.</span><span class="n">cp</span><span class="p">:</span> <span class="n">shutil</span><span class="o">.</span><span class="n">copy2</span><span class="p">(</span><span class="n">src_file</span><span class="p">,</span> <span class="n">dst_file</span><span class="p">)</span> <span class="k">else</span><span class="p">:</span> <span class="n">shutil</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="n">src_file</span><span class="p">,</span> <span class="n">dst_file</span><span class="p">)</span> <span class="n">processed</span> <span class="o">=</span> <span class="mi">1</span> <span class="k">return</span> <span class="n">processed</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">exists</span><span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">dst</span><span class="p">):</span> <span class="n">os</span><span class="o">.</span><span class="n">mkdir</span><span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">dst</span><span class="p">)</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">args</span><span class="o">.</span><span class="n">src</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="c"># ignore files without extension (can have the same name as the ext)</span> <span class="n">file_ext</span> <span class="o">=</span> <span class="n">fil</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">fil</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="p">))</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="k">else</span> <span class="bp">None</span> <span class="c"># ignore dots in given extensions</span> <span class="n">extensions</span> <span class="o">=</span> <span class="p">[</span><span class="n">ext</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="p">,</span> <span class="s">&#39;&#39;</span><span class="p">)</span> <span class="k">for</span> <span class="n">ext</span> <span class="ow">in</span> <span class="n">args</span><span class="o">.</span><span class="n">ext</span><span class="p">]</span> <span class="k">if</span> <span class="n">file_ext</span> <span class="ow">in</span> <span class="n">extensions</span><span class="p">:</span> <span class="n">count</span> <span class="o">+=</span> <span class="n">process</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">opt</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="s">&quot;{0}{1}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">rm</span><span class="p">),</span> <span class="nb">int</span><span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">cp</span><span class="p">)),</span> <span class="mi">2</span><span class="p">)</span> <span class="n">log</span> <span class="o">=</span> <span class="s">&quot;Files {0}: {1}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">({</span><span class="mi">0</span><span class="p">:</span><span class="s">&quot;moved&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">:</span><span class="s">&quot;copied&quot;</span><span class="p">,</span> <span class="mi">2</span><span class="p">:</span><span class="s">&quot;removed&quot;</span><span class="p">}[</span><span class="n">opt</span><span class="p">],</span> <span class="n">count</span><span class="p">)</span> <span class="k">return</span> <span class="n">log</span> </pre></div> <p>Este script nos permite realizar las mismas operaciones que los comandos anteriores y además tiene dos ventajas adicionales:</p> <ul> <li> <p><strong>Permite emplear varias extensiones a la vez</strong>, lo que nos permite mover/copiar/borrar distintos tipos de archivo en una sola operación. Por ejemplo si queremos mover todos los tipos de imagen de la estructura de directorios anterior, podríamos hacerlo indicándole que emplee las extensiones <code>.jpg</code>, <code>.gif</code> y <code>.png</code> en la misma operación. El comando sería el siguiente (las extensiones pueden llevar o no el punto, funciona exactamente igual), ejecutado desde el directorio raíz:</p> <p><code>python move_by_ext.py jpg .gif png -d ./dir_5</code></p> </li> <li> <p><strong>Funciona en Windows</strong>. A diferencia del comando para Linux, este se puede emplear en Windows sin necesidad de instalar ningún entorno que nos simule un shell Linux (como <a href="http://www.cygwin.com/">Cygwin</a>) y solo es necesario tener instalado Python 2.7</p> </li> </ul> <p>La sintaxis del script es muy sencilla y se puede ver reflejada en la ayuda del mismo:</p> <div class="codehilite"><pre><span class="gp">$</span> python move_by_ext.py -h <span class="go">usage: move_by_ext.py ext [-s SRC] [-d DST] [-c | -r] [--help]</span> <span class="go">Move (or copy/remove) all files selected by extension into a directory tree to</span> <span class="go">a destination directory.</span> <span class="go">positional arguments:</span> <span class="go"> ext the extension(s) of the files to process. To use more</span> <span class="go"> than one extension, separate them with a space</span> <span class="go">optional arguments:</span> <span class="go"> -h, --help show this help message and exit</span> <span class="go"> -s SRC, --src SRC the source path. Current dir if none is provided</span> <span class="go"> -d DST, --dst DST the destination path. Current dir if none is provided</span> <span class="go"> -c, --copy copy all the files with the given extension(s) to the</span> <span class="go"> destination directory.</span> <span class="go"> -r, --remove remove all the files with the given extension(s). Use</span> <span class="go"> with caution! remove also in the subdirectories</span> <span class="go"> -v, --version show program&#39;s version number and exit</span> </pre></div> <p>El script completo puede encontrarse en el repositorio <em>Python Recipes</em> alojado en <a href="http://github.com/joedicastro/python-recipes">github</a>.</p>joe di castroTue, 26 Apr 2011 23:17:00 +0200http://joedicastro.com/mover-todos-los-archivos-del-mismo-tipo-de-un-arbol-de-directorios-a-la-vez.htmlpythonscriptmovercopiarborrarlinuxwindows