martes, 31 de mayo de 2016

Sincronizando archivos y directorios

Una estrategia común para mantener una copia de seguridad de un sistema implica mantener uno o más directorios sincronizados con otro directorio (o directorios) localizados en el sistema local (normalmente algún tipo de dispositivo extraíble de almacenamiento) o en un sistema remoto. Podríamos, por ejemplo, tener una copia local de un sitio web en desarrollo y sincronizarlo de vez en cuando con la copia "live" en un servidor web remoto.

En el mundo tipo Unix, la herramienta preferida para esta tarea es rsync. Este programa puede sincronizar tanto directorios locales como remotos usando el protocolo de actualización remota (remote-update protocol) rsync, que permite que rsync detecte rápidamente las diferencias entre los dos directorios y realice la cantidad mínima requerida de copiado para sincronizarlos. Esto hace a rsync muy rápido y económico de usar, comparado con otros tipos de programas de copiado.

rsync se invoca así:

rsync opciones origen destino

donde origen y destino son uno de los siguientes:
  • Un archivo o directorio local
  • Un archivo o directorio remoto en la forma [usuario@]host:ruta
  • Un servidor rsync remoto especificado con una URI de rsync://[usuario@]host[:puerto]/ruta
Fíjate que o el origen o el destino deben ser un archivo local. La copia remoto-a-remoto no está soportada.

Probemos rsync en archivos locales. Primero, limpiemos nuestro directorio foo:

[me@linuxbox ~]$ rm -rf foo/*

A continuación, sincronizaremos el directorio playground con su copia correspondiente en foo:

[me@linuxbox ~]$ rsync -av playground foo

Hemos incluido tanto la opción -a (para archivar - que produce recursividad y conservación de los atributos de los archivos) como la opción -v (salida detallada) para hacer un espejo del directorio playground dentro de foo. Mientras el comando se ejecuta, veremos una lista de los archivos y directorios que se están copiando. Al final, veremos un mensaje de resumen como este:

sent 135759 bytes received 57870 bytes 387258.00 bytes/sec
total size is 3230 speedup is 0.02

indicando la cantidad de copia realizada. Si ejecutamos el comando de nuevo, veremos un resultado diferente:

[me@linuxbox ~]$ rsync -av playgound foo
building file list ... done
sent 22635 bytes received 20 bytes 45310.00 bytes/sec
total size is 3230 speedup is 0.14

Fíjate que no había listado de archivos. Esto es porque rsync detectó que no había diferencias entre ~/playground y ~/foo/playground, y por tanto no necesitaba copiar nada. Si modificamos un archivo en playground y ejecutamos rsync de nuevo:

[me@linuxbox ~]$ touch playground/dir-099/file-Z
[me@linuxbox ~]$ rsync -av playground foo
building file list ... done
playground/dir-099/file-Z
sent 22685 bytes received 42 bytes 45454.00 bytes/sec
total size is 3230 speedup is 0.14

vemos que rsync detectó el cambio y copió sólo el archivo actualizado.

Como ejemplo práctico, consideremos el disco duro externo imaginario que usamos anteriormente con tar. Si conectamos el disco a nuestro sistema y, de nuevo, se monta en /media/BigDisk, podemos realizar una útil copia de seguridad del sistema creando primero un directorio llamado /backup en el disco externo, y luego usando rsync para copiar lo más importante de nuestro sistema en el disco externo:

[me@linuxbox ~]$ mkdir /media/BigDisk/backup
[me@linuxbox ~]$ sudo rsync -av --delete /etc /home /usr/local /media/BigDisk/backup

En este ejemplo, hemos copiado los directorios /etc, /home, y /usr/local de nuestro sistema a nuestro dispositivo de almacenamiento imaginario. Hemos incluido la opción --delete para eliminar archivos que existieran en el dispositivo de copia y que ya no existieran en el dispositivo original (esto es irrelevante la primera vez que hacemos una copia de seguridad, pero será útil en sucesivas copias). Repitiendo el procedimiento de conectar el disco externo y ejecutar este comando rsync sería una forma útil (aunque no ideal) de mantener un pequeño sistema respaldado. De acuerdo, un alias también aquí sería muy útil: Podríamos crear un alias y añadirlo a nuestro archivo .bashrc para proporcionar esta característica:

alias backup='sudo rsync -av --delete /etc /home /usr/local
/media/BigDisk/backup'

Ahora, todo lo que tenemos que hacer es conectar nuestro disco externo y ejecutar el comando backup para realizar el trabajo.

lunes, 30 de mayo de 2016

zip

El programa zip es tanto una herramienta de compresión como de empaquetado. El formato de archivo usado por el programa es familiar para los usuarios de Windows, ya que lee y escribe archivos .zip. En Linux, sin embargo, gzip es el programa de compresión predominante con bzip2 en un cercano segundo lugar.

En su uso más básico, zip se invoca así:

zip opciones archivozip archivo...

Por ejemplo, para hacer un archivo zip de nuestro patio de juegos, haríamos ésto:

[me@linuxbox ~]$ zip -r playground.zip playground

A menos que incluyamos la opción -r para recursividad, sólo se almacenará el directorio playground (pero no su contenido). Aunque la extensión .zip se añade automáticamente, la indicaremos por claridad.

Durante la creación del archivo zip, zip normalmente mostrará una serie de mensajes como éstos:

adding: playground/dir-020/file-Z (stored 0%)
adding: playground/dir-020/file-Y (stored 0%)
adding: playground/dir-020/file-X (stored 0%)
adding: playground/dir-087/ (stored 0%)
adding: playground/dir-087/file-S (stored 0%)

Estos mensajes muestran el estado de cada archivo añadido al paquete. zip añadirá archivos al paquete usando uno de estos dos métodos de almacenamiento: O "almacenará" un archivo sin compresión, como hemos visto aquí, o "reducirá" el archivo realizando compresión. El valor numérico mostrado tras el método de almacenamiento indica la cantidad de compresión realizada. Como nuestro patio de juegos sólo contiene archivos vacíos, no se realizará ninguna compresión de su contenido.

Extraer el contenido de un zip es sencillo cuando usamos el programa unzip:

[me@linuxbox ~]$ cd foo
[me@linuxbox foo]$ unzip ../playground.zip

Una cosa a tener en cuenta sobre zip (al contrario que en tar) es que si se especifica un archivo existente, se actualiza en vez de reemplazarse. Ésto significa que el archivo existente se conserva, pero los archivos nuevos se añaden y los coincidentes se reemplazan.

Los archivos pueden listarse y extraerse selectivamente de un archivo zip especificándolo a unzip:

[me@linuxbox ~]$ unzip -l playground.zip playground/dir-087/file-Z
Archive: ../playground.zip
 Length    Date   Time  Name
--------   ----   ----  ----
       0 10-05-08 09:25 playground/dir-087/file-Z
--------                -------
       0                1 file
[me@linuxbox ~]$ cd foo
[me@linuxbox foo]$ unzip ../playground.zip playground/dir-087/file-Z
Archive: ../playground.zip
replace playground/dir-087/file-Z? [y]es, [n]o, [A]ll, [N]one, [r]ename: y
extracting: playground/dir-087/file-Z

Usar la opción -l hace que unzip sólo liste el contenido de un paquete sin extraer nada. Si no se especifican archivos, unzip listará todos los archivos del paquete. La opción -v puede añadirse para aumentar la información del listado. Fíjate que cuando la extracción del paquete entra en conflicto con un archivo existente, el usuario es preguntado antes de que el archivo sea reemplazado.

Como tar, zip puede hacer uso de la entrada y la salida estándar, aunque su implementación no es tan útil. Es posible canalizar una lista de nombres de archivo a zip con la opción -@:

[me@linuxbox foo]$ cd
[me@linuxbox ~]$ find playground -name "file-A" | zip -@ file-A.zip

Aquí usamos find para generar una lista de archivos coincidentes con el test -name "file-A", y luego canalizamos la lista en zip, lo que crea el paquete file-A.zip que contiene los archivos seleccionados.

zip también soporta escribir su salida a la salida estándar, pero su uso es limitado porque muy pocos programas pueden hacer uso de la salida. Desafortunadamente, el programa unzip no acepta entrada estándar. Ésto impide que zip y unzip se usen juntos para realizar copiado de archivos en red como tar.

zip puede, sin embargo, aceptar entrada estándar, así que puede usarse para comprimir la salida de otros programas:

[me@linuxbox ~]$ ls -l /etc/ | zip ls-etc.zip -
adding: - (deflated 80%)

En este ejemplo canalizamos la salida de ls en zip. Al igual que tar, zip interpreta el guión final como "usa la entrada estándar para el archivo de entrada."

El programa unzip permite que su salida se envíe a la salida estándar cuando se especifica la opción -p (para canalizar):

[me@linuxbox ~]$ unzip -p ls-etc.zip | less

Hemos visto algunas de las cosas básicas que puede hacer zip/unzip. Ambos tienen un montón de opciones que aumentan su flexibilidad, aunque algunas son específicas para plataformas de otros sistemas. Las man pages de zip y unzip son muy buenas y contienen ejemplos útiles. Sin embargo, el uso principal de estos programas es el intercambio de archivos con sistemas Windows, en lugar de comprimir y empaquetar en Linux, donde tar y gzip son claramente preferidos.

viernes, 27 de mayo de 2016

tar

En el mundo del software tipo Unix, el programa tar es la herramienta clásica para empaquetar. Su nombre, abreviatura de tape archive, revela sus raíces como una herramienta para hacer copias de seguridad en cinta. Aunque aún se usa para su tarea tradicional, está igualmente adaptada en otros dispositivos de almacenamiento.

A menudo vemos nombres de archivo que terminan con la extensión .tar o .tgz, que indican un paquete tar "plano" y un paquete comprimido con gzip, respectivamente. Un paquete tar puede consistir en un grupo de archivos separados, una o más jerarquías de directorios, o una mezcla de ambas. La sintaxis del comando funciona así:

tar modo[opciones] ruta...

donde modo es uno de los siguientes modos de operación (sólo se muestra una lista parcial; mira la man page de tar para ver la lista completa):

Tabla 18-2: Modos de tar
Modo Descripción
c Crea un paquete de una lista de archivos y/o directorios.
x Extrae un archivo.
r Añade rutas específicas al final de un archivo.
t Lista el contenido de un paquete.

tar usa una forma un poco extraña para expresar las opciones, así que necesitaremos algunos ejemplos para mostrar como funciona. Primero, recreemos nuestro patio de juegos del capítulo anterior:

[me@linuxbox ~]$ mkdir -p playground/dir-{001..100}
[me@linuxbox ~]$ touch playground/dir-{001..100}/file-{A..Z}

A continuación, creemos un paquete tar del patio de juegos completo:

[me@linuxbox ~]$ tar cf playground.tar playground

Este comando crea un paquete tar llamado playground.tar que contiene la jerarquía completa del directorio playground. Podemos ver que el modo y la opción f, que hemos usado para especificar el nombre del paquete tar, pueden ir juntas, y no requieren un guión delante. Fíjate, sin embargo, que el modo siempre debe ser especificado primero, antes que ninguna opción.

Para listar los contenidos del paquete, podemos hacer ésto:

[me@linuxbox ~]$ tar tf playground.tar

Para un listado más detallado, podemos añadir la opción v (verbose):

[me@linuxbox ~]$ tar tvf playground.tar

Ahora, extraigamos el patio de juegos en una nueva localización. Lo haremos creando un nuevo directorio llamado foo, cambiando el directorio y extrayendo el paquete tar:

[me@linuxbox ~]$ mkdir foo
[me@linuxbox ~]$ cd foo
[me@linuxbox foo]$ tar xf ../playground.tar
[me@linuxbox foo]$ ls
playground

Si examinamos el contenido de ~/foo/playground, vemos que el paquete ha sido instalado exitosamente, creando un reproducción precisa de los archivos originales. Sin embargo, hay una advertencia: A menos que estés operando como superusuario, los archivos y los directorios extraídos de paquetes toman la propiedad del usuario que realiza la restauración, en lugar del propietario original.

Otro comportamiento interesante de tar es la forma en que maneja las rutas en los archivos. Por defecto son relativas, en lugar de absolutas. tar hace ésto simplemente eliminando cualquier barra al principio de las rutas cuando creamos el archivo. Para demostralo, volvemos a crear nuestro paquete, esta vez especificando una ruta absoluta:

[me@linuxbox foo]$ cd
[me@linuxbox ~]$ tar cf playground2.tar ~/playground

Recuerda, ~/playground se expandirá en /home/me/playground cuando pulsemos la tecla intro, así tendremos una ruta absoluta para nuestra demostración. A continuación, extraeremos el paquete como antes y veremos que ocurre:

[me@linuxbox ~]$ cd foo
[me@linuxbox foo]$ tar xf ../playground2.tar
[me@linuxbox foo]$ ls
home playground
[me@linuxbox foo]$ ls home
me
[me@linuxbox foo]$ ls home/me
playground

Aquí podemos ver que cuando extrajimos nuestro segundo paquete, volvió a crear el directorio home/me/playground relativo a nuestro directorio de trabajo actual, ~/foo, no relativo al directorio root, como hubiese sido con rutas absolutas. Esto podría parecer una forma extraña de trabajar, pero en realidad es más útil de esta manera, ya que nos permite extraer paquetes en cualquier sitio en lugar de ser forzados a extraerlos en sus localizaciones originales. Repitiendo el ejercicio con la inclusión de la opción verbose (v) nos dará una imagen más clara de lo que está pasando.

Consideremos un ejemplo hipotético, aunque práctico, de tar en acción. Imagina que queremos copiar el directorio home y su contenido de un sistema a otro y tenemos un gran disco duro USB que usaremos para la transferencia. En nuestro sistema Linux moderno, la unidad se monta "automágicamente" en el directorio /media. Imaginemos también que el disco tiene nombre de volumen BigDisk cuando lo conectamos. Para crear el paquete tar, podemos hacer lo siguiente:

[me@linuxbox ~]$ sudo tar cf /media/BigDisk/home.tar /home

Después de que se escriba el paquete tar, desmontamos el disco y lo conectamos al segundo ordenador. De nuevo, se monta en /media/BigDisk. Para extraer el paquete, hacemos lo siguiente:

[me@linuxbox2 ~]$ cd /
[me@linuxbox2 /]$ sudo tar xf /media/BigDisk/home.tar

Lo que es importante ver aquí es que primero tenemos que cambiar el directorio a /, para que la extracción sea relativa al directorio root, ya que todas las rutas dentro del paquete son relativas.

Cuando extraemos un paquete, es posible limitar qué se extrae de él. Por ejemplo, si queremos extraer un archivo individual de un paquete, podría hacerse así:

tar xf paquete.tar ruta

Añadiendo la ruta al final del comando, tar sólo restaurará el archivo especificado. Pueden especificarse múltiples rutas. Fíjate que la ruta deber ser exactamente igual a la ruta almacenada en el paquete. Cuando especificamos rutas, los comodines generalmente no están soportados; sin embargo, la versión GNU de tar (que es la versión que encontramos más a menudo en las distribuciones Linux) los admite con la opción --wildcards. Aquí tenemos un ejemplo usando nuestro paquete playground.tar anterior:

[me@linuxbox ~]$ cd foo
[me@linuxbox foo]$ tar xf ../playground2.tar --wildcards 'home/me/playground/dir-*/file-A'

Este comando extraerá sólo los archivos que coincidan con la ruta especificada incluyendo el comodín dir-*.

tar se usa a menudo junto con find para producir paquetes. En este ejemplo, usaremos find para producir un conjunto de archivos e incluirlos en un paquete:

[me@linuxbox ~]$ find playground -name 'file-A' -exec tar rf playground.tar '{}' '+'

Aquí usamos find para buscar todos los archivos de playground llamados file-A y luego, usando la acción -exec, invocamos a tar en el modo inclusión (r) para añadir los archivos buscados al paquete playground.tar.

Usar tar con find es una buena forma de crear copias de seguridad incrementales de un árbol de directorios o de un sistema completo. Usando find para buscar archivos más nuevos que un archivo con una marca de tiempo, podríamos crear una paquete que sólo contenga archivos más nuevos que el último archivo, asumiendo que el archivo con la marca de tiempo se ha actualizado justo después de que se cree cada paquete.

tar puede también usar tanto entrada estándar como salida estándar. Aquí hay un ejemplo detallado:

[me@linuxbox foo]$ cd
[me@linuxbox ~]$ find playground -name 'file-A' | tar cf - --files-from=- | gzip > playground.tgz

En este ejemplo, usamos el programa find para producir una lista de archivos coincidentes y canalizamos a través de tar. Si se especifica el nombre de archivo "-", toma el significado de entrada o salida estándar, según sea necesario (Por cierto, este acuerdo de usar "-" para representar entrada/salida estándar se usa también en muchos otros programas). La opción --files-from (que también puede especificarse como -T) hace que tar lea su lista de rutas de un archivo en lugar de la línea de comandos. Finalmente, el paquete producido por tar es canalizado a gzip para crear el paquete comprimido playground.tgz. La extensión .tgz es la extensión convencional dada a los paquetes tar comprimidos con gzip. A veces, también se usa la extensión .tar.gz.

Aunque hemos usado el programa gzip externamente para producir nuestro paquete comprimido, las versiones modernas de GNU tar soportan tanto compresión gzip como bzip2 directamente, con el uso de las opciones z y j, respectivamente. Usando nuestro ejemplo anterior como base, podemos simplificarlo de esta forma:

[me@linuxbox ~]$ find playground -name 'file-A' | tar czf
playground.tgz -T -

Si hubiéramos querido crear un paquete bzip2 comprimido, podríamos haber hecho esto:

[me@linuxbox ~]$ find playground -name 'file-A' | tar cjf playground.tbz -T -

Simplemente cambiando la opción de compresión de z a j (y cambiando la extensión del archivo de salida a .tbz para indicar que es un archivo comprimido con bzip2) habilitamos la compresión bzip2.

Otro uso interesante de la entrada y salida estándar con el comando tar implica transferir archivos entre sistemas a través de una red. Imagina que tenemos dos máquinas ejecutando un sistema tipo Unix equipadas con tar y ssh. En tal escenario, podríamos transferir un directorio de un sistema remoto (llamado remote-sys para este ejemplo) a nuestro sistema local:

[me@linuxbox ~]$ mkdir remote-stuff
[me@linuxbox ~]$ cd remote-stuff
[me@linuxbox remote-stuff]$ ssh remote-sys 'tar cf - Documents' | tar xf -
me@remote-sys’s password:
[me@linuxbox remote-stuff]$ ls
Documents

Así podríamos copiar un directorio llamado Documents de un sistema remoto remote-sys a un directorio incluido en el directorio llamado remote-stuff en el sistema local. ¿Cómo hacemos ésto? Primero, ejecutamos el programa tar en el sistema remoto usando ssh. Recordarás que ssh nos permite ejecutar un programa remotamente en un ordenador conectado a una red y "ver" el resultado en el sistema local: la salida estándar producida en el sistema remoto se envía al sistema local para visualizarla. Podemos aprovechar esto haciendo que tar cree un paquete (el modo c) y lo envíe a la salida estándar, en lugar de a un archivo (la opción f con el guión como argumento), de esta forma transportamos el archivo a través de un túnel encriptado proporcionado por ssh en el sistema local. En el sistema local, ejecutamos tar y hacemos expandir un paquete (el modo x) suministrado desde la entrada estándar (de nuevo con la opción f con el guión como argumento).

jueves, 26 de mayo de 2016

Empaquetando archivos

Una tarea de gestión de archivos que se usa a menudo junto con la compresión es el empaquetado. Empaquetar es el proceso de recoger muchos archivos y agruparlos en un gran archivo único. El empaquetado a menudo se hace como parte de los sistemas de copia de seguridad. También se usa cuando se mueven datos antiguos de un sistema a algún tipo de almacenamiento de largo plazo.

miércoles, 25 de mayo de 2016

No seas un compresor compulsivo

Ocasionalmente veo gente tratando de comprimir un archivo, que ya ha sido comprimido con un algoritmo de compresión efectivo, haciendo algo así:

$ gzip picture.jpg

No lo hagas. ¡Probablemente sólo estarás malgastando tiempo y espacio! Si aplicas compresión a un archivo que ya ha sido comprimido, en realidad acabarás obteniendo un archivo más grande. Esto es porque todas las técnicas de compresión incluyen información adicional que se añade al archivo para describir la compresión. Si tratas de comprimir un archivo que ya no contiene información redundante, la compresión no proporcionará ningún ahorro para compensar la información adicional.

martes, 24 de mayo de 2016

bzip2

El programa bzip2, de Julian Seward, es similar a gzip, pero usa un algoritmo de compresión diferente que consigue niveles de compresión más altos a costa de la velocidad de compresión. En la mayoría de los aspectos, funciona de la misma forma que gzip. Un archivo comprimido con bzip2 es marcado con la extensión .bz2:

[me@linuxbox ~]$ ls -l /etc > foo.txt
[me@linuxbox ~]$ ls -l foo.txt
-rw-r--r-- 1 me me 15738 2008-10-17 13:51 foo.txt
[me@linuxbox ~]$ bzip2 foo.txt
[me@linuxbox ~]$ ls -l foo.txt.bz2
-rw-r--r-- 1 me me 2792 2008-10-17 13:51 foo.txt.bz2
[me@linuxbox ~]$ bunzip2 foo.txt.bz2

Como podemos ver, bzip2 puede usarse de la misma forma que gzip. Todas las opciones (excepto -r) que vimos para gzip también son soportadas en bzip2. Fíjate, sin embargo, que la opción del nivel de compresión (-number) tiene un significado algo diferente para bzip2. bzip2 viene con bunzip2 y bzcat para descomprimir los archivos.

bzip2 también viene con el programa bzip2recover, que tratará de recuperar archivos .bz2 dañados.

lunes, 23 de mayo de 2016

gzip

El programa gzip se usa para comprimir uno o más archivos. Cuando lo ejecutamos, reemplaza el archivo original por una versión comprimida del original. El programa correspondiente gunzip se usa para restaurar archivos comprimidos a su forma original descomprimida. Aquí tenemos un ejemplo:

[me@linuxbox ~]$ ls -l /etc > foo.txt
[me@linuxbox ~]$ ls -l foo.*
-rw-r--r-- 1 me me 15738 2008-10-14 07:15 foo.txt
[me@linuxbox ~]$ gzip foo.txt
[me@linuxbox ~]$ ls -l foo.*
-rw-r--r-- 1 me me 3230 2008-10-14 07:15 foo.txt.gz
[me@linuxbox ~]$ gunzip foo.txt
[me@linuxbox ~]$ ls -l foo.*
-rw-r--r-- 1 me me 15738 2008-10-14 07:15 foo.txt

En este ejemplo, creamos un archivo de texto llamado foo.txt desde un listado de directorio. A continuación, ejecutamos gzip, que reemplaza el archivo original con una versión comprimida llamada foo.txt.gz. En el listado del directorio de foo.*, vemos que el archivo original ha sido reemplazado con la versión comprimida, y la versión comprimida tiene más o menos la quinta parte del tamaño de la original. También podemos ver que el archivo comprimido tiene los mismos permisos y datos de tiempo que el original.

A continuación, ejecutamos el programa gunzip para descomprimir el archivo. Tras ello, podemos ver que la versión comprimida del archivo ha sido reemplazada por la original, de nuevo con los permisos y datos de tiempo conservados.

gzip tiene muchas opciones. Aquí tenemos unas pocas:

Tabla 18-1: Opciones de gzip
Opción Descripción
-c Escribe la salida en la salida estándar y mantiene los archivos originales. También puede especificarse con –-stdout y –-to-stdout.
-d Descomprime. Hace que gzip actúe como gunzip. También puede indicarse con –-decompress o –-uncompress.
-f Fuerza la compresión incluso si ya existe una versión comprimida del archivo original. También puede indicarse con --force.
-h Muestra información de uso. También puede especificarse con --help.
-l Lista las estadísticas de compresión para cada archivo comprimido. También puede especificarse con --list.
-r Si uno o más argumentos de la línea de comandos son directorios, comprime recursivamente los archivos incluidos en ellos. También podría especificarse con --recursive.
-t Prueba la integridad de un archivo comprimido. También podría especificarse con --test.
-v Muestra mensajes mientras comprime. También puede indicarse con --verbose.
-number Establece la cantidad de compresión. number es un entero en el rango entre 1 (más rápido, menos comprimido) y 9 (más lento, más comprimido). Los valores 1 y 9 también pueden expresarse como --fast y --best, respectivamente. El valor por defecto es 6.

Volviendo a nuestro ejemplo anterior:

[me@linuxbox ~]$ gzip foo.txt
[me@linuxbox ~]$ gzip -tv foo.txt.gz
foo.txt.gz: OK
[me@linuxbox ~]$ gzip -d foo.txt.gz

Aquí, hemos reemplazado el archivo foo.txt con una versión comprimida llamada foo.txt.gz. A continuación, hemos probado la integridad de la versión comprimida, usando las opciones -t y -v. Finalmente hemos descomprimido el archivo de vuelta a su forma original.

gzip también puede usarse de formas interesantes vía entrada y salida estándar:

[me@linuxbox ~]$ ls -l /etc | gzip > foo.txt.gz

Este comando crea una versión comprimida de un listado de directorios.

El programa gunzip, que descomprime archivos gzip, asume que los nombres de archivo terminan con la extensión .gz, así que no es necesario especificarla, siempre que el nombre especificado no entre en conflicto con un archivo descomprimido existente.

[me@linuxbox ~]$ gunzip foo.txt

Si nuestro objetivo fuera sólo ver el contenido de un archivo de texto comprimido, podríamos hacer ésto:

[me@linuxbox ~]$ gunzip -c foo.txt | less

Alternativamente, hay un programa proporcionado con gzip, llamado zcat, que es equivalente a gunzip con la opción -c. Puede usarse como el comando cat en archivos comprimidos con gzip:

[me@linuxbox ~]$ zcat foo.txt.gz | less

Consejo: También existe el programa zless. Realiza la misma función que la tubería anterior.

viernes, 20 de mayo de 2016

Comprimiendo archivos

A lo largo de la historia de la informática, ha existido una lucha por almacenar la mayor cantidad de datos posible en el mínimo espacio disponible, ya sea espacio en memoria, dispositivos de almacenamiento o ancho de banda. Muchos de los servicios de datos que hoy consideramos normales como reproductores de música portátiles, televisiones de alta definición o Internet de banda ancha, deben su existencia a las eficaces técnicas de compresión de datos.

La compresión de datos es el proceso de eliminar la redundancia de los datos. Consideremos un ejemplo imaginario. Digamos que tenemos una foto completamente negra con una dimensiones de 100 por 100 píxeles. En términos de almacenamiento de datos (asumiendo 24 bits, o 3 bytes por píxel), la imagen ocuparía 30.000 bytes de almacenamiento:

100 * 100 * 3 = 30.000

Una imagen que es toda de un color contiene datos completamente redundantes. Si fuéramos listos, podríamos codificar los datos de tal forma que simplemente describiríamos el hecho de que tenemos un bloque de 10.000 píxeles negros. Así que, en lugar de almacenar un bloque de datos que contenga 30.000 ceros (el negro se representa normalmente en archivos de imagen como cero), podríamos comprimir los datos al número 10.000, seguido de un cero para representar nuestros datos. Este sistema de compresión de datos se llama run-length enconding y es una de las técnicas de compresión más rudimentarias. Las técnicas de hoy son mucho más avanzadas y complejas pero el objetivo básico sigue siendo el mismo, librarse de los datos redundantes.

Los algoritmos de compresión (las técnicas matemáticas usada para llevar a cabo la compresión) se clasifican en dos categorías, lossless (sin pérdida) y lossy (con pérdida). La compresión sin pérdida mantiene todos los datos contenidos en el original. Esto significa que cuando un archivo es restaurado desde una versión comprimida, el archivo restaurado es exactamente el mismo que la versión original sin comprimir. La compresión con pérdida, por otra parte, elimina datos al realizar la compresión, para permitir que se aplique una mayor compresión. Cuando se restaura un archivo con pérdida, no coincide con la versión original; más bien, es una aproximación muy parecida. Ejemplos de compresión con pérdida son JPG (para imágenes) y MP3 (para música). En este tema, veremos sólo la compresión sin pérdida, ya que la mayoría de los datos de un ordenador no pueden tolerar ninguna pérdida de datos.

jueves, 19 de mayo de 2016

Archivado y copias de seguridad

Una de las tareas principales de un administrador de sistemas es mantener la seguridad de los datos del sistema. Una forma de hacerlo es realizando copias de seguridad periódicas de los archivos del sistema. Incluso si no eres el administrador del sistema, a menudo es útil hacer copias de las cosas y mover grandes colecciones de archivos de un lugar a otro y de un dispositivo a otro.

En este capítulo, veremos varios de los programas comunes usados para manejar colecciones de archivos. Están los programas de compresión de archivos:
  • gzip – Comprime y descomprime archivos
  • bzip2 –  Compresor de archivos por ordenación de bloques
Los programas de archivado:
  • tar – Utilidad de archivado en cinta
  • zip – Empaqueta y comprime archivos
Y el programa de sincronización de archivos:
  • rsync – Sincronización remota de archivos y directorios

miércoles, 18 de mayo de 2016

Para saber más

  • Los programas locate, updatedb, find y xargs son parte del paquete GNU Project's findutils. El proyecto GNU ofrece un sitio web con una extensa documentación on-line, que es muy buena y deberías leerla si usas estos programas en entornos de alta seguridad:
    http://www.gnu.org/software/findutils/

martes, 17 de mayo de 2016

Resumiendo

Es fácil ver que locate es simple y que find es complicado. Ambos tienen sus usos. Tómate tiempo para explorar las muchas funciones de find. Puede, usándolo regularmente, mejorar tu entendimiento de las operaciones de los sistemas de archivo Linux.

lunes, 16 de mayo de 2016

Opciones

Finalmente, tenemos las opciones. Las opciones se usan para controlar el alcance de una búsqueda con find. Se pueden incluir con otros test y acciones cuando se construyen expresiones con find. Aquí hay una lista de las más usadas:

Tabla 17-7: Opciones find
Opción Descripción
-depth Dirige find a procesar los archivos de un directorio antes del propio directorio. Esta opción se aplica automáticamente cuando se especifica la acción -delete.
-maxdepth levels Establece el número máximo de niveles que find desciende en un árbol de directorios cuando realizamos tests y acciones.
-mindepth levels Establece el número mínimo de niveles que find desciende en un árbol de directorios antes de aplicar tests y acciones.
-mount Dirige find para no recorrer directorios que estén montados en otros sistemas de archivos.
-noleaf Dirige find a no optimizar su búsqueda asumiendo que está buscando en un sistema de archivos tipo Unix. Ésto es necesario cuando buscamos en sistemas de archivos DOS/Windows y CD-ROMs

viernes, 13 de mayo de 2016

Una vuelta al patio de juegos

Es hora de usar find de forma (casi) práctica. Crearemos un patio de juegos y probaremos algo de lo que hemos aprendido.

Primero, creemos un patio de juegos con muchos subdirectorios y archivos:

[me@linuxbox ~]$ mkdir -p playground/dir-{001..100}
[me@linuxbox ~]$ touch playground/dir-{001..100}/file-{A..Z}

¡Maravíllense con el poder de la línea de comandos! Con estas dos líneas, hemos creado un directorio patio de juegos que contiene 100 subdirectorios, cada uno conteniendo 26 archivos vacíos. ¡Inténtalo con la GUI!

El método que hemos empleado para realizar esta magia incluye un comando familiar (mkdir), una exótica expansión de shell (llaves) y un nuevo comando, touch. Combinando mkdir con la opción -p (que hace que mkdir cree los directorios padres de las rutas especificadas) con la expansión con llaves, podemos crear 100 subdirectorios.

El comando touch se usa normalmente para establecer o actualizar la fecha de acceso, la fecha de creación y la fecha de modificación de los archivos. Sin embargo, si el argumento del nombre de archivo es el de un archivo que no existe, se crea un archivo vacío.

En nuestro patio de juegos, hemos creado 100 instancias de un archivo llamado file-A. Encontrémoslas:

[me@linuxbox ~]$ find playground -type f -name 'file-A'

Fijate que al contrario que ls, find no produce resultados ordenados. Su orden lo determina el dispositivo de almacenamiento. Podemos confirmar que realmente tenemos 100 instancias del archivo de esta forma:

[me@linuxbox ~]$ find playground -type f -name 'file-A' | wc -l
100

A continuación, veamos los archivos encontrados basándonos en su hora de modificación. Ésto será útil cuando creamos copias de seguridad u organizamos archivos en orden cronológico. Para hacerlo, primero crearemos un archivo de referencia contra el que compararemos la hora de modificación:

[me@linuxbox ~]$ touch playground/timestamp

Esto crea un archivo vacío llamado timestamp y establece su hora de modificación en la hora actual. Podemos verificarlo usando otro comando útil, stat, que es un tipo de versión vitaminada de ls. El comando stat revela todo lo que el sistema entiende sobre un archivo y sus atributos:

[me@linuxbox ~]$ stat playground/timestamp
File: `playground/timestamp'
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 803h/2051d Inode: 14265061 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1001/ me) Gid: ( 1001/ me)
Access: 2008-10-08 15:15:39.000000000 -0400
Modify: 2008-10-08 15:15:39.000000000 -0400
Change: 2008-10-08 15:15:39.000000000 -0400

Si “tocamos” el archivo de nuevo y luego lo examinamos con stat, veremos que las horas del archivo se han actualizado:

[me@linuxbox ~]$ touch playground/timestamp
[me@linuxbox ~]$ stat playground/timestamp
File: `playground/timestamp'
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 803h/2051d Inode: 14265061 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1001/ me) Gid: ( 1001/ me)
Access: 2008-10-08 15:23:33.000000000 -0400
Modify: 2008-10-08 15:23:33.000000000 -0400
Change: 2008-10-08 15:23:33.000000000 -0400

A continuación, usemos find para actualizar algunos archivos de nuestro patio de juegos:

[me@linuxbox ~]$ find playground -type f -name 'file-B' -exec touch '{}' ';'

Esto actualiza todos los archivos del patio de juegos llamados file-B. Luego usaremos find para identificar los archivos actualizados comparando todos los archivos con el archivo de referencia timestamp:

[me@linuxbox ~]$ find playground -type f -newer playground/timestamp

El resultado contiene las 100 instancias del archivo file-B. Como realizamos un touch en todos los archivos del patio de juegos llamados file-B, después de actualizar timestamp, ahora son más “nuevos” que timestamp y por lo tanto pueden ser identificados con el test -newer.

Finalmente, volvamos al test de malos permisos que realizamos anteriormente y apliquémoslo a playground:

[me@linuxbox ~]$ find playground \( -type f -not -perm 0600 \) -or \(-type d -not -perm 0700 \)

Este comando lista los 100 directorios y los 2600 archivos de playground (así como timestamp y el propio playground, haciendo un total de 2702) porque ninguno de ellos entra en nuestra definición de “buenos permisos”. Con nuestro conocimiento de los operadores y las acciones, podemos añadir acciones a este comando para aplicar nuevos permisos a los archivos y directorios en nuestro patio de juegos:

[me@linuxbox ~]$ find playground \( -type f -not -perm 0600 -exec chmod 0600 '{}' ';' \) -or \( -type d -not -perm 0700 -exec chmod 0700 '{}' ';' \)

En el día a día, podríamos encontrar más fácil manejarnos con dos comandos, uno para los directorios y otro para los archivos, en lugar de este gran comando complejo, pero es bueno saber que podemos hacerlo de esta forma. Lo importante aquí es entender cómo pueden usarse los operadores y las acciones juntos para realizar tareas útiles.

jueves, 12 de mayo de 2016

Tratando con nombres de archivo simpáticos

Los sistemas tipo Unix permiten la inclusión de espacios (¡e incluso nuevas líneas!) en los nombres de archivo. Esto causa problemas a programas como xargs que construyen listas de argumentos para otros programas. El espacio incluido será tratado como un delimitador, y el comando resultante interpretará cada palabra separada por un espacio como un argumento separado. Para remediar ésto, find y xarg permiten el uso opcional de un carácter nulo como separador de argumentos. Un carácter nulo se define en ASCII como el carácter representado por el número cero (en lugar de, por ejemplo, el carácter espacio, que se define en ASCII como el carácter representado por el número 32). El comando find cuenta con la acción -print0, que produce una salida separada por nulos, y el comando xargs tiene la opción --null, que acepta entradas separadas por nulos. Aquí tenemos un ejemplo:

find ~ -iname '*.jpg' -print0 | xargs --null ls -l

Usando esta técnica, podemos asegurarnos que todos los archivos, incluso los que contienen espacios en sus nombres, son tratados correctamente.

miércoles, 11 de mayo de 2016

xargs

El comando xargs realiza una función interesante. Acepta entradas de la entrada estándar y las convierte en una lista de argumentos para el comando especificado. Con nuestro ejemplo, podríamos usarlo así:

find ~ -type f -name 'foo*' -print | xargs ls -l
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me   0 2008-09-19 12:53 /home/me/foo.txt

Aquí vemos la salida del comando find canalizada al comando xargs que, a su vez, construye una lista de argumentos para el comando ls y luego lo ejecuta.

Nota: Si bien el número de argumentos que pueden colocarse en una línea de comandos es bastante largo, no es ilimitado. Es posible crear comandos que son demasiado largos para que los acepte el shell. Cuando una línea de comandos excede la longitud máxima soportada por el sistema, xargs ejecuta el comando especificado con el máximo número de argumentos posible, y luego repite el proceso hasta que se agote la entrada estándar. Para ver el tamaño máximo de la línea de comandos, ejecuta xargs con la opción --show limits.

martes, 10 de mayo de 2016

Mejorando la eficiencia

Cuando usamos la acción -exec, arranca una nueva instancia del comando especificado cada vez que encuentra un archivo coincidente. Hay veces que podríamos preferir combinar todos los resultados de la búsqueda y arrancar una instancia única del comando. Por ejemplo, en lugar de ejecutar los comandos así:

ls -l archivo1
ls -l archivo2

podríamos preferir ejecutarlos así:

ls -l archivo1 archivo2

ésto hace que el comando se ejecute sólo una vez en lugar de muchas veces. Hay dos formas de hacer ésto. La forma tradicional, usando el comando externo xargs, y la forma alternativa, usando una nueva característica del propio find. Hablaremos sobre la forma alternativa primero.

Cambiando el punto y coma del final por un signo más, activamos la capacidad de find de combinar los resultados de la búsqueda en una lista de argumentos para una ejecución única del comando deseado. Volviendo a nuestro ejemplo, ésto:

find ~ -type f -name 'foo*' -exec ls -l '{}' ';'
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me   0 2008-09-19 12:53 /home/me/foo.txt

ejecutará ls cada vez que encuentre un archivo coincidente. Cambiando el comando a:

find ~ -type f -name 'foo*' -exec ls -l '{}' +
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me   0 2008-09-19 12:53 /home/me/foo.txt

tendremos los mismos resultados, pero el sistema sólo habrá ejecutado el comando ls una vez.

lunes, 9 de mayo de 2016

Acciones definidas por el usuario

Además de las acciones predefinidas, podemos invocar también comandos arbitrarios. La forma tradicional de hacerlo es con la acción -exec. Esta acción funciona así:

-exec comando {} ;

donde comando es el nombre de un comando, {} es la representación simbólica de la ruta actual, y el punto y coma es un delimitador requerido indicando el final del comando. Aquí hay un ejemplo de usar -exec de forma que funcione como la acción -delete que vimos antes:

-exec rm '{}' ';'

De nuevo, como las llaves y el punto y coma tiene significado especial para el shell, deben ir entrecomillados o “escapados”.

También es posible ejecutar una acción definida por el usuario interactivamente. Usando la acción -ok en lugar de -exec, el usuario es preguntado antes de la ejecución de cada comando especificado:

find ~ -type f -name 'foo*' -ok ls -l '{}' ';'
< ls ... /home/me/bin/foo > ? y
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
< ls ... /home/me/foo.txt > ? y
-rw-r--r-- 1 me me 0 2008-09-19 12:53 /home/me/foo.txt

En este ejemplo, buscamos archivos con nombres que empiecen con la cadena “foo” y ejecutamos el comando ls -l cada vez que encontremos uno. Al usar la acción -ok se pregunta al usuario antes de que se ejecute el comando ls.

viernes, 6 de mayo de 2016

Acciones predefinidas

¡Trabajemos! Tener una lista de resultados de nuestro comando find es útil, pero lo que realmente queremos hacer es actuar en los elementos de la lista. Afortunadamente, find permite que las acciones se realicen basándose en los resultados de búsqueda. Hay una serie de acciones predefinidas y varias formas de aplicar acciones definidas por el usuario. Primero, veamos algunas acciones predefinidas:

Tabla 17-6: Acciones predefinidas de find
Acción
Descripción
-delete Borra el archivo buscado.
-ls Realiza el equivalente a ls -dils en el archivo buscado. La salida se envía a la salida estándar.
-print Envía la ruta completa del archivo buscado a la salida estándar. Es la acción por defecto si no se especifica ninguna otra acción.
-quit Sale una vez que una búsqueda se ha realizado.

Como con los tests, hay muchas más acciones. Mira la man page de find para más detalles.

En nuestro primer ejemplo, hicimos esto:

find ~

que produjo una lista de cada archivo y subdirectorio contenidos en nuestro directorio home. Produjo una lista porque la acción -print está implícita si no se especifica ninguna otra acción. Por lo tanto, nuestro comando podría expresarse así también:

find ~ -print

Podemos usar find para borrar archivos que cumplan ciertos criterios. Por ejemplo, para borrar archivos que tengan la extensión “.BAK” (que a menudo se usa para designar archivos de copia de seguridad), podríamos usar este comando:

find ~ -type f -name '*.BAK' -delete

En este ejemplo, cada archivo en el directorio home del usuario (y sus subdirectorios) es buscado por si su nombre termina en .BAK. Cuando se encuentran, se borran.

Atención: No debería seguir sin decir que tengas extremo cuidado cuando uses la acción -delete. Siempre prueba antes el comando sustituyendo la acción -print por -delete para confirmar los resultados de búsqueda.

Antes de continuar, echemos otro vistazo a cómo afectan a las acciones los operadores lógicos. Considera el siguiente comando:

find ~ -type f -name '*.BAK' -print

Como hemos visto, este comando buscará todos los archivos normales (-type f) cuyos nombres terminan en .BAK (-name '*.BAK') y enviará la ruta relativa de cada archivo coincidente a la salida estándar (-print). Sin embargo, la razón de que el comando funcione de la manera que lo hace viene determinada por la relación lógica entre cada test y su acción. Recuerda que, por defecto, hay una relación -and implícita entre cada test y cada acción. Podríamos expresar también el comando de esta forma para hacer la relación lógica más fácil de ver:

find ~ -type f -and -name '*.BAK' -and -print

Con nuestro comando expresado completamente, veamos cómo afectan los operadores lógicos a su ejecución:

Test/Acción
Se ejecuta sólo si...
-print -type f-name '*.BAK' son verdaderos
-name ‘*.BAK’ -type f es verdadero
-type f Se ejecuta siempre, ya que es el primer test/acción en una relación -and.

Como la relación lógica entre los tests y las acciones determina cuales de ellos se ejecutan, podemos ver que el orden de los test y acciones es importante. Por ejemplo, si fuéramos a reordenar los test y las acciones para que la acción -print fuera la primera, el comando se comportaría de forma muy diferente:

find ~ -print -and -type f -and -name '*.BAK'

Esta versión del comando imprimirá cada archivo (la acción -print siempre se evalúa como verdadera) y luego busca por tipo de archivo y la extensión de archivo especificada.