2.2.2. Ficheros y directorios

2.2.2.1. Bicheando

2.2.2.1.1. … dentro de directorios

Nuestra intención ahora es echarle un vistazo a los comandos que nos permiten tanto comprobar cuál es el contenido de un fichero como comprobar cuál es el contenido de un directorio.

Por supuesto, para hacer referencia a ficheros y directorios es indispensable conocer cómo referirse a ellos.

pwd

Nos permite conocer en qué directorio estamos:

$ pwd
/home/usuario
ls

El comando fundamental para ver el contenido de un directorio es ls:

ls [opciones] [<nombre1> [<nombre2> ...]]

Su sintaxis permite añadir opciones (muchísimas) y el directorio (también fichero que queremos consultar). Pueden incluirse varios y en ese caso se mostrarán todos ellos. Por ejemplo:

$ ls /sbin

Mostrará el contenido del directorio /sbin. Al ser un directorio es posible también acabar el nombre con la barra:

$ ls /sbin/

Nota

Dependiendo del contexto (o sea, del programa que estemos ejecutando) será o no indiferente rematar el nombre del directorio con la barra. En el caso de ls, sólo hay diferencia cuando el fichero es un enlace simbólico que apunta a un directorio: si se añade la barra, se mostrará el contenido del directorio apuntado; y si no se añade, el propio enlace simbólico.

También se puede usar ls con un fichero:

$ ls /bin/cp
/bin/cp

En este caso, se muestra el nombre del fichero con lo que, usado así y si no se usan comodines o expandibles, no tiene más utilidad que saber si el fichero existe. Una opción que le da sentido a mirar un fichero con ls es -l, que permite obtener cierta información del fichero o directorio que se consulte:

$ ls -l /bin/cp
-rwxr-xr-x 1 root root 124932 ene 14  2015 /bin/cp

Para cada fichero que consultemos, ls nos devuelve:

  • El tipo de fichero, representado por el primer carácter:

    Carácter

    Tipo de fichero

    -

    Fichero regular

    d

    Directorio

    l

    Enlace simbólico

    p

    Tubería

    b

    Dispositivo de bloques

    c

    Dispositivo de caracteres

    s

    Socket

  • Los nueve siguientes caracteres representan los permisos, que se tratarán más adelante.

  • El siguiente campo, que es un número, representa el número de referencias al fichero en el sistema de ficheros. Para entender esto, tomemos primero un fichero vacío (que podemos crear con touch):

    $ touch vacio
    $ ls -l vacio
    -rw-rw-r-- 1 usuario usuario 0 oct 14 10:02 vacio
    

    Este fichero recién creado ocupará un espacio físico en disco y sólo tiene una referencia en el sistema de ficheros: a través del nombre vacío. Consecuentemente el número de enlaces es 1. Sin embargo, en los sistemas unix existen los llamados enlaces duros, que permiten asociar ese mismo espacio de disco a otro nombre (véanse ln o cp más adelante):

    $ ln vacio mismo.vacio
    $ ls *vacio -l
    -rw-rw-r-- 2 profesor profesor 0 oct 14 10:02 mismo.vacio
    -rw-rw-r-- 2 profesor profesor 0 oct 14 10:02 vacio
    

    Ahora hay dos nombres refiriéndose al mismo espacio. Por tanto, aparece un 2.

    En el caso de los directorios, el concepto es el mismo (referencias al propio directorio), aunque no está asociado al concepto de enlaces duros. En principio, al crear un directorio:

    $ mkdir directorio
    

    hay dos referencias a él: la referencia a él que se hace en el directorio raíz:

    $ ls -d directorio
    directorio
    

    y la referencia a él que hace él mismo (el punto .):

    $ ls -a directorio
    . ..
    

    Consecuentemente, al crearse un directorio habrá dos referencias a él:

    $ ls -ld directorio
    drwxrwxr-x 2 usuario usuario 4096 oct 14 12:38 directorio
    

    Y cada vez que creemos un subdirectorio, se generará otra referencia:

    $ mkdir directorio/subdirectorio
    $ ls directorio/subdirectorio
    . ..
    

    En este caso, los dos puntos (..) de directorio/subdirectorio, al referirse a su directorio padre, se refieren a directorio:

    $ ls -d directorio
    drwxrwxr-x 3 usuario usuario 4096 oct 14 12:38 directorio
    

    En cambio, crear un nuevo fichero dentro del directorio no provoca ninguna referencia:

    $ touch directorio/otro.vacio.mas
    $ ls -d directorio
    drwxrwxr-x 3 usuario usuario 4096 oct 14 12:38 directorio
    

    En conclusión, el número representa:

    • Para ficheros, representa el número de enlaces (duros) al propio fichero.

    • Para directorios, el número de subdirectorios que contiene más 2.

  • Los dos siguientes campos son el usuario propietario y el grupo propietario, cuya explicación diferiremos hasta explicar los permisos

  • El número que se muestra a continuación es el tamaño.

  • El siguiente dato es la fecha de modificación.

  • El último campo el nombre del fichero.

Otra opción útil es -d. El comportamiento de ls es mostrar el propio fichero, si es un fichero; o el contenido del directorio, si es un directorio. Esta opción permite ver el propio directorio, y no su contenido y puede ser útil si lo que buscamos es conocer información sobre él (permisos o propietarios, por ejemplo):

$ ls -ld /
drwxr-xr-x 22 root root 4096 jul 21  2015 /

-l muestra el tamaño en bytes, lo cual es engorroso cuando el tamaño es grande. Para paliar esto, existe la opción -h, que representa el tamaño en las unidades más adecuadas:

$ ls -lh Documentos
-rw-rw-r-- 1 usuario usuario  473K jun 24 09:11 BOJA1.pdf
-rw-rw-r-- 1 usuario usuario  2,6M oct  6 12:50 CuadranteFotos.pdf
-rw-rw-r-- 1 usuario usuario   30K oct  5 17:53 Distribucion.pdf

Por defecto, ls ordena los ficheros alfabéticamente, pero en ocasiones puede resultarnos útil otro tipo de ordenación:

  • -r invierte el orden.

  • -t ordena de más reciente a más antiguo. Es obvio, que si se quieren ver los más recientes al final del listado, puede usarse -tr.

  • -X ordena alfabéticamente según la extensión.

  • -S ordena por tamaño de mayor a menor.

  • group-directories-first muestra antes los directorios que los ficheros.

stat

Además de ls -l otro modo de obtener información sobre ficheros es el comando stat:

$ stat .
Fichero: «.»
   Tamaño: 4096       Bloques: 8          Bloque E/S: 4096   directorio
   Dispositivo: 801h/2049d Nodo-i: 524290      Enlaces: 36
Acceso: (0755/drwxr-xr-x)  Uid: ( 1000/ usuario)   Gid: ( 1000/ usuario)
Acceso: 2016-10-14 13:32:41.571887432 +0200
Modificación: 2016-10-14 13:36:51.769082058 +0200
      Cambio: 2016-10-14 13:36:51.769082058 +0200
    Creación: -

Este comando es más prolijo que la opción -l de ls y entre otras cosas ofrece cuatro fechas distintas:

  • La fecha de modificación que indica el último momento en que se modificó el contenido de un fichero.

  • La fecha de acceso que indica el último momento en que se accedió (leyó) el fichero. Esta es la teoría, porque en la práctica hacer registro en el disco duro cada vez que se accedió a un fichero o directorio provoca que baje mucho el rendimiento del sistema, así que lo habitual es que linux monte los sistemas de ficheros con la opción noatime, para que la fecha de acceso sólo se actualice o una vez al día o cuando se modifica el fichero.

  • La fecha de cambio que indica el último momento en que se cambiaron los atributos de un fichero (propietarios, permisos, etc.).

  • La fecha de creación que indica el momento en que se creó el fichero. En este caso, el sistema de ficheros es capaz de almacenarlo, pero los sistemas unix no hacen uso de esta fecha, de modo que siempre la veremos vacía.

Una opción interesante de stat es -c que permite indicar el formato de salida del resultado de la consulta. Para expresar este formato cada propiedad se representa de la forma %<letra>. Por ejemplo, %U representa el nombre del propietario y %u, uid del propietario. Así pues:

$ stat -c "%U/%u" fichero
usuario/1000

En la página del manual se desglosan las letras que corresponden a cada propiedad.

tree

Muestra los contenidos de un directorio en forma arborescente:

tree [<opciones>] [<directorio>]

El directorio que se indica como argumento es aquel del que se desean mostrar los contenidos. Si no se indica se sobrentiende el directorio actual. Dispone de muchas opciones. Algunas interesantes son:

-d

Sólo muestra directorios y no ficheros.

-L

Nivel de profundidad. tree es recursivo, así que muestra los directorios y los directorios de los directorios y así sucesivamente hasta que ya no quedan directorios que mostrar. Con esta opción podemos indicar en qué nivel parar. Por ejemplo:

$ tree -dL 1 /

Sólo nos mostrará los subdirectorios del directorio raíz, sin profundizar más.

-a

Como la opción de ls, muestra también los ficheros ocultos.

Nota

Requiere la instalación del paquete homónimo.

file

Por lo general, las extensiones se usan para saber de qué tipo es un fichero, pero no tienen por qué haber sido bien elegidas. file es un comando que intenta averiguar de qué tipo es, basándose en su contenido:

$ file primera_carlista.mp4
primera_carlista.mp4: ISO Media, MP4 Base Media v1 [IS0 14496-12:2003]

Si se usa la opción -i, devuelve el tipo mime:

$ file primera_carlista.mp4
primera_carlista.mp4: video/mp4; charset=binary
which

Dentro de los comandos que nos permiten gulismear en el árbol de directorios está which. Básicamente, busca dónde se encuentran los programas que podemos ejecutar a través de la línea de comandos1:

$ which ls
/bin/ls

Una alternativa (que es muy útil al programar porque cumple con el estándar POSIX) es:

$ command -v ls
/bin/ls
whereis

ls puede usarse para comprobar si un fichero se encuentra o no en un directorio. Sin embargo, si no sabemos a ciencia cierta en qué directorio se encuentra, el comando puede resultar o tedioso o, directamente, bastante inútil. Ya se ha citado which, pero está limitado a ejecutables. Un comando más exhaustivo es whereis, que es capaz de encontrar ejecutables, códigos fuente y páginas del manual en los directorios en los que suelen encontrarse:

$ whereis ls
ls: /bin/ls /usr/share/man/man1/ls.1.gz
find

Sin embargo, whereis no es suficiente si lo que queremos es buscar un fichero cualquiera. Para ello puede usarse el comando find que permite hacer una búsqueda bastante exhaustiva y, por tanto, tiene una ingente cantidad de opciones.

Nota

Los argumentos de find no siguen el estándar POSIX.

En general, la sintaxis del comando es la siguiente:

find [-P|-H|-L] [<directorio>] [opciones]

Las tras primeras opciones hacen referencia a cómo find tratará los enlaces simbólicos. la predeterminada es -P que implica no seguirlos.

El directorio es aquel bajo el cual find buscará lo que se le exprese con las opciones que se pasen a continuación. La búsqueda es recursiva, de modo que find buscará dentro de los subdirectorios. El orden es importante y, de hecho, el directorio debe expresarse antes que las opciones. Si no se indica, se sobrentiende que es el directorio actual (o sea, .).

Las opciones son muy variadas: algunas permiten modificar el comportamiento del comando; otras seleccionar ficheros casi según cualquier criterio imaginable; y otras realizar una operación (como imprimir por pantalla o borrar). El orden no es indistinto y deben colocarse según se han enunciado: primero las de modificación, luego las de selección y, por último, la de operación. Si no se especifica ninguna acción, se sobreentiende que se desea imprimir (-print). En este documento sólo se describirán las opciones más relevantes:

Opciones de modificación:

-maxdepth <N>:

Realiza la búsqueda sólo profundizando hasta el nivel N. Por ejemplo, un nivel 1 buscará dentro del directorio que se haya usado como punto de partido, pero no dentro de los subdirectorios. Esta opción debe colocarse antes de

-mount:

No busca en subdirectorios que pertenezcan a sistemas de ficheros distintos al del directorio de punto de partida.

Opciones de selección:

Estas opciones se evalúan a verdadero o falso. Si la evaluación sobre un fichero resulta verdadera, el fichero se seleccionará.

-name <expresion>:

permite buscar ficheros por su nombre según la expresión que se indique. Esta expresión sigue las mismas reglas que las indicadas aquí. La variante -iname obra del mismo modo, pero sin tener en cuenta mayúsculas o minúsculas.

-path <expresion>:

busca ficheros atendiendo a toda su ruta y no sólo al nombre. Existe también la variante ipath para no distinguir mayúsculas de minúsculas. En cuanto a la ruta, debe considerarse que esta se expresa como ruta relativa al directorio que se tome como punto de partida. Por ejemplo, si el directorio es . las rutas de todos los ficheros encontrados empezarán por ./; si dir, todas empezarán por dir/.

-regex <expresion_regular>:

Como -path pero la expresión se evaluará como una expresión regular. iregex cumple la misma función pero sin distinguir mayúsculas de minúsculas. Relacionada con estas dos expresiones existe la opción -regexptype, que permite indicar qué tipo de expresiones regulares se usarán. Para saber qué tipos estan soportados puede hacerse lo siguiente:

$ find -regextype help
-type f|d|l|p|b|c|s:

restringe la búsqueda al tipo de fichero indicado. El carácter es el mismo que el que muestra la opción -l de ls, excepto en el caso de los ficheros regulares que se usa la f en vez del guión.`

-samefile <fichero>:

busca los otros enlaces duros del fichero indicado.

-empty:

el fichero (o directorio) está vacío.

-mtime <n>:

el fichero se modificó hace n días. Si se hace negativo el número, significa hace menos de n días; y, si incluye un signo + explícito, hace más de n días. Las opciones -atime y -ctime sirven para lo mismo pero usando las fechas de acceso y cambio.

-newer <fichero>:

busca los ficheros modificados después del fichero de referencia proporcionado como argumento. anewer y cnewer sirven para lo mismo, pero usando las fechas de acceso y cambio.

-group <grupo>:

busca ficheros cuyo grupo propietario sea el indicado (puede usarse el nombre o el gid).

-user <usuario>:

busca ficheros propiedad del usuario indicado (puede usarse el usuario o el uid).

-links <n>:

busca ficheros que tengan n enlaces. Pueden usarse los signos - y + con el mismo significado que para mtime.

-size <n><unidad>:

busca ficheros de un tamaño determinado. En cuanto a la cantidad pueden usarse los signos - y + para indicar menor y mayor como en otros casos. En cuanto a la unidad se pueden indicar:

  • b, bloques de 512 bytes.

  • c, bytes.

  • k, kilobytes.

  • M, megabytes.

  • G, gigabytes.

Opciones de operación:

Pueden indicarse varias operaciones que se ejecutarán una detrás de otra.

-print:

imprime en pantalla los ficheros seleccionados, uno por línea. Hay variantes de esta opción, pero hay una indispensable en ciertos casos que es print0 que imprime de modo que el carácter que separa un fichero de otro es el carácter nulo.

-delete:

borra el fichero indicado. Si el fichero es un directorio, debe estar vacío.

-exec <comando> '{}' +:

ejecuta el comando indicado sobre los ficheros seleccionados. El “{}” representa tales ficheros que se usaran como argumentos para comando. Al incluir el signo +, no se ejecuta un comando por cada fichero, sino que se procura ejecutar el comando incluyendo el mayor número de ficheros posibles en cada invocación. Por ejemplo:

$ print . -type f -exec file -i '{}' +
./hola2:         inode/x-empty; charset=binary
./a 1:           inode/x-empty; charset=binary
./tmux.txt:      text/x-fortran; charset=utf-8
./hola:          inode/x-empty; charset=binary
-ok <command> '{}' \;:

ejecuta el comando para cada uno de los ficheros seleccionados, pero pregunta antes si se quiere efectuar cada operación o no. Existe también una versión de -exec que remata en punto y coma, pero por ser menos eficiente que la referida, se ha callado.

-prune:

no hace nada en especial más que impedir que find descienda si el fichero seleccionado es un directorio. Puede usarse para evitar revisar los contenidos de un fichero.

-quit:

sale inmediatamente de find

Faltan opciones, pero con las indicadas hay materia para entretenerse. Aún queda por explicar los operadores, pero antes es útil ilustrar con algunos ejemplos el uso:

  1. Buscar todos los ficheros .pdf bajo el directorio de trabajo:

    $ find -iname '*.pdf'
    

    Como no se especifica acción alguna, se sobreentiende -print.

  2. Lo mismo, pero especificando que se buscan ficheros regulares y no cualquier otra cosa (enlaces simbólicos, por ejemplo):

    $ find -type f -iname '*.pdf'
    
  3. Buscar un fichero llamado deseable.txt bajo el directorio de trabajo y para la búsqueda nada más encontrarlo:

    $ find -type f -name 'deseable.txt' -print -quit
    

    En este caso, para cada fichero encontrado, primero se imprime y luego se acaba la ejecución. Por tanto, nada más encontrar e imprimir el primer fichero, se dejará de buscar. Puede ser útil si sabemos de antemano que el fichero es único.

  4. Muestra el tipo de los ficheros del directorio actual:

    $ find -maxdepth 1 -type f -exec file '{}' +
    
  5. Borra los ficheros vacíos que se encuentran bajo el directorio actual:

    $ find -type f -empty -delete
    
  6. Como en el caso anterior, pero preguntando primero:

    $ find -type f -empty -ok rm -f '{}' \;
    
  7. Busca bajo el directorio /home/store/video los ficheros mayores a 500MB:

    $ find /home/store/video -type f -size +500M
    
  8. Busca bajo el directorio actual los ficheros con más de un enlace:

    $ find -type f -links +1
    

Estas pueden ser búsquedas útiles con find, pero… no se vayan todavía, que aún hay más. Si se analizan algunos ejemplos, se observará que se han dado dos o más condiciones o incluso dos o más acciones y estas siempre eran acumulativas:

$ find -type f -empty -delete

En este ejemplo se buscan ficheros que sean regulares (-type f) y que, además, estén vacios. Es decir, que si hablamos en términos lógicos hay un opèrador and implícito entre una y otra condición. También lo hay entre las condiciones y la acción -delete (y me lo imprimes), porque las acciones, además de hacer algo, devuelven un valor siempre verdadero.

Pues bien, en find podemos usar los operadores -a (and), -o (or) o ! (not) y los paréntesis para agrupar. Esto convierte a find en infinitamente más poderoso. Por ejemplo:

  1. Busca todos los ficheros que no estén vacíos:

    $ find -empty -o -print
    

    La razón de que este funcione es que find evalúa como habitualmente se hace en programación cuando hay un or: sólo se evalua la segunda expresión si la primera era falsa. Por tanto, si el fichero está vacío no se hace nada, pues no hay ninguna acción indicada y ahi se para de evaluar para el fichero en cuestión; pero si no está vacío, se evalúa la segunda expresión y, consecuentemente, se imprime el nombre del fichero.

    Por supuesto esto mismo se podría haber logrado así:

    $ find \! -empty -print
    

    que es más immediato. pero hacerlo de otro modo ilustra mejor cómo evalúa find. En este último caso, ademäs, podemos ahorrarnos -print.

  2. Busca ficheros .pdf bajo el directorio actual, pero desecha los subdirectorios que se llaman backup:

    $ find -type d -name backup -prune -o -name '*.pdf' -print
    

    El razonamiento es análogo al del anterior ejemplo, pero en este caso ha sido necesario añadir -prune. La razón es que el hecho de que no se imprima un directorio, no implica que no se entre en él y se revisen sus contenidos, por lo que es necesaria la acción -prune para descartarlo por completo. Antes se omitió este -prune, porque al concordar la expresión con directorios vacíos, esto implica que no tengan nada dentro.

  3. Busca ficheros regulares y enlaces simbólicos:

    $ find \( -type f -o -type l \) -print
    

    Es necesario agrupar para que ambas condiciones se apliquen a la misma acción -print. No incluir los paréntesis implicaría no hacer nada para los ficheros regulares y, en caso contrario, comprobar que es un enlace simbólico e imprimir, o sea, imprimir sólo enlaces simbólicos.

Nota

find puede ser muy útil a la hora de seleccionar ficheros de los que se desea realizar copia de seguridad al ser mucho más versátil que las opciones que tar da para ello.

du

Sirve para estimar el espacio de disco que ocupan los directorios (o ficheros) que se le pasan como argumento. Entiéndase que el espacio de ocupación que proporciona para los directorios es el espacio que ocupan todos sus contenidos. Por defecto, desglosa el espacio de cada uno de los subdirectorios que se encuentra, así que por lo general es usarlo con la opción -s que devuelve solamente el espacio ocupado por el fichero que se le ha dado como argumento. Además dispone de una opción -h que se comporta como la de ls. Por tanto:

$ du -hs ~
3,4G  /home/josem

Nos devolverá el espacio total ocupado por nuestro directorio personal. Es útil añadir la opción -x que no tiene en cuenta los subdirectorios que monten otros sistemas de ficheros.

2.2.2.1.2. … y dentro de ficheros

cat

El comando más simple que permite consultar el contenido de un fichero es cat: simplemente escupe por pantalla, carácter a carácter, el contenido. Si el fichero es texto plano podremos leerlo, si es un fichero binario veremos caracteres ininteligibles y, si nuestra intención era echar un vistazo, habrá servidor de poco:

$ cat saludo.txt
Hola

Suponiendo que hubiéramos escrito previamente un fichero que contuviera únicamente la palabra Hola. Si se escriben dos o más ficheros como argumento, cat los concatena:

$ cat saludo.txt despedida.txt
Hola
Adiós

Es posible también usarlo sin indicar fichero alguno: en este caso recibirá los datos a través de la entrada estándar (véase redirecciones).

De entre sus opciones, es útil a veces -n, que numera las líneas al mostrarlas.

more

Sin embargo, cat tiene el inconveniente de mostrar el fichero de una sola tacada, por lo que si el fichero es largo se escapará por la parte superior de la pantalla. Para remediarlo, puede usarse more que es un paginador que al llenarse la pantalla hará una pausa:

$ more fichero_larguisimo

Al llenar la pantalla, el programa quedará esperando a que pulsemos Enter (avancar una línea) o Espacio (avanzar una pantalla). También puede escribirse una barra (/) seguida de una expresión regular para buscar la primera ocurrencia que concuerde con dicha expresión,

Se saldrá del programa al acabarse el fichero o al pulsarse q.

less

Como con more sólo podemos avanzar, pero no retroceder, existe less. Tiene un uso semejante, con la diferencia de que hay teclas para ir hacia atrás. Consúltese la página del manual.

Tanto cat, como more, como less suelen tener ejecutables equivalentes para mostrar ficheros que hayan sido comprimidos sin necesidad de descomprimirlos previamente:

  • zcat, zmore y zless para comprimidos con gzip (extensión .gz).

  • bzcat, bzmore y bzless para comprimidos con bzip2 (extensión .bz o .bzip2).

  • xzcat, xzmore y xzless para comprimidos con xz (extensión .xz).

head

Cuando no se quiere inspeccionar todo el fichero sino sólo su comienzo, es útil el comando head, que permite indicar cuáles de las primeras líneas de un texto se desean mostrar:

$ head /etc/passwd

Si no se especifica nada, se muestran 10 líneas. Si se quiere mostrar un número distinto es posible hacerlo con la opción -n:

$ head -n20 /etc/passwd

El ejemplo, mostrará 20 líneas. Es posible también mostrar todas las líneas menos las n últimas, haciendo negativo el argumento de -n:

$ head -n-1 /etc/passwd

Esto mostraría todas las líneas menos la última.

Como con los comandos anteriores, si no se indica nombre alguno de fichero, se intenta tomar datos de la entrada estándar.

tail

Como contrapartida a este último comand, tail permite mostrar las últimas líneas del fichero. Si no se especifica cuántas:

$ tail /etc/passwd

mostrará las diez últimas, pero puede variarse este número usando la opción -n:

$ tail -n15 /etc/passwd

Si se incluye antes del número el signo +, se motrará a partir de dicha línea:

$ tail -n+15 /etc/passwd

De nuevo, no indicar fichero implica que se quiere leer de la entrda estándar.

tail, además, admite la opción -f, que es muy útil cuando se quiere monitorizar porque muestra las últimas 10 líneas del fichero (o cualquier otro número si se usa además -n) y queda esperando a que el fichero se actualice con nuevas líneas:

# tail -f /var/log/syslog
watch

No es un comando propiamente para ver el contenido de un fichero, sino para refrescar cada cierto tiempo (por defecto cada dos segundos) la salida por pantalla de una orden. Así si hacemos:

$ watch -n1 "cat /proc/mdstat"

nos mostrará el contenido del fichero /proc/mdstat (ya que eso es lo que hace cat) e ira refrescando ese contenido cada segundo. Por la misma razón:

$ watch "ls -l /var/log"

mostrará el contenido del directorio /var/log e irá refrescado cada dos segundos la salida.

2.2.2.2. Creando contenidos

mkdir

La acción más sencilla para alterar nuestro sistema de ficheros es crear un nuevo directorio:

$ mkdir nuevo

Se pueden indicar varios directorios en una misma línea:

$ mkdir nuevo nuevo/dentro otro.nuevo

Obsérvese que para crear nuevo/dentro es necesario que exista previamente file:nuevo, de ahí el orden. Sin embargo, mkdir tiene la opción -p que crear un directorio, creando previamente todos los directorios necesarios:

$ mkdir -p  nuevo/dentro
rmdir

En contraposición, rmdir elimina directorios, pero sólo si están vacíos:

$ rmdir nuevo/dentro

SI se usa la opción -p borra directorios padre, si estos se quedan vacíos:

$ rmdir -p nuevo/dentro
touch

En general, para crear un fichero de un determinado tipo lo que debe hacerse es usar una aplicación adecuada. Ahora bien, cuando se quiere crear un fichero vacío, muy comúnmente se usa touch:

$ touch estoy.vacio
$ ls -l estoy.vacio
-rw-r--r-- 1 usuario usuario 0 oct 15 20:08 estoy.vacio

En realidad, esta no es la función principal de touch, sino un efecto secundario, consecuencia de que el archivo no existe. Su tarea principal es cambiar las fechas de un archivo: o la de modificación (-m) o la de acceso (-a) o ambas a la vez (no incluir ninguna de las anteriores opciones).

Las fechas se fijan al momento en que se ejecuta el comando, a menos que se indique una fecha distinta a través de -t. En todo caso, La de cambio siempre será la del momento de la acción:

$ touch -t 200109110915 estoy.vacio
$ stat estoy.vacio
  Fichero: «estoy.vacio»
    Tamaño: 0             Bloques: 0          Bloque E/S: 1024   fichero
    regular vacío
  Dispositivo: fd02h/64770d       Nodo-i: 28          Enlaces: 1
  Acceso: (0644/-rw-r--r--)  Uid: ( 1000/ usuario)   Gid: ( 1000/ usuario)
        Acceso: 2001-09-11 09:15:00.000000000 +0200
  Modificación: 2001-09-11 09:15:00.000000000 +0200
        Cambio: 2016-10-15 20:31:07.000000000 +0200
      Creación: -
rm

Si, en vez de crear un fichero, queremos borrarlo, podemos usar el comando rm. Su forma más elemental de uso es indicar como argumento el nombre del fichero a borrar:

$ rm fichero_a_borrar

aunque pueden incluirse varios nombres en una misma orden.

Como por error podemos borrar un fichero sin querer, rm proporciona la opción -i, que pide confirmación antes de hacerlo. En oposición está -f, que no lo hace. Cuando se usan ambas prevalece la última que se indicó.

Además, el comando sirve en principio para borrar ficheros, por lo que si se intenta borrar un directorio, fallará. Para evitar esto, puede usarse el comando -r que borrará directorios con todo su contenido:

$ rm -r directorio

Es habitual usar -r junto a -f para evitar preguntas continuas de confirmación, ya que la orden recursiva puede implicar muchísimos borrados.

Advertencia

Es sumamente aconsejable colocar -rf al final de la orden después de haber incluido las rutas de borrado, y no antes. Eso evita que accidentalmente pulsemos Enter antes de acabar de terminar de escribir la ruta y borremos accidentalmente más de la cuenta.

mv

Otra operación habitual con ficheros (o directorios) es moverlos de lugar, para lo cual existe el comando mv. La sintaxis general es la siguiente:

$ mv [opciones] <origen> <destino>

Es decir, el fichero se mueve desde un origen a un destino. El origen es el propio fichero, mientras que el destino puede existir o no. Para entender cómo se comporta mv es mejor usar el siguiente ejemplo:

$ touch fic1 fic2
$ mkdir dir1 dir2

Es decir, creamos dos ficheros y dos directorios. En este caso:

$ mv fic1 dir1
$ ls dir1
fic1

el destino existe y es un directorio, por lo que el efecto que movemos fic1 dentro del directorio de destino dir1. En cambio, si el destino no existe:

$ mv dir1/fic1 dir2/fic.renombrado
$ ls dir2
fic.renombrado

Lo que hacemos es cambiar el nombre. Por último, si el destino existe, pero es un fichero, sobrescribimos:

$ mv dir2/fic.renombrado fic2
ls dir1 dir2 fic2

Si tomáramos como origen un directorio en vez de un fichero las consecuencias son las mismas, excepto que no tiene sentido el caso en que el destino es un fichero, porque un directorio no puede sobrescribir un fichero, así que la orden fallará.

También es posible indicar más de dos argumentos. En ese caso el último argumento se considerará el destino y todos los anteriores ficheros de origen. Para que la orden funcione el destino debe ser un directorio y existir; así lo que haremos será mover todos los orígenes dentro de tal directorio de destino.

Resumidamente, podemos verlo así:

Origen

Destino

Acción

F

F

El fichero se mueve al destino. Si este existe, sobreescribe; si no, cambia de nombre.

F

D

El fichero se mueve dentro del directorio.

D

F

Error.

D

D

El origen se mueve dentro del directorio de destino. Si no existe, cambia el nombre, a menos que al destino le incluyamos la barra final, en cuyo caso se producirá un error.

Al igual que rm, mv tiene también las opciones -i y -f.

Nota

En los sistemas BSD, cuando el origen es un directorio y acaba en barra, ewl comportamiento de mv (y de cp), cambia. Cuando el directorio de destino existe, no se mueve el propio directorio origen dentro del de destino, sino su contenido.

cp

Si lo que se pretende es hacer una copia, de manera que al acabar tengamos el original y una copia, existe el comando cp. SU funcionamiento es similar al de mv excepto por hecho de que no perdemos el archivo original. Por tanto, es válido todo lo ya indicado, pero, además, dispone de algunas otras opciones útiles.

Cuando movemos un fichero, no cambia sus propiedades (fechas, permisos, propietarios, etc.), excepto la fecha de cambio. Por contra, cuando se copia un fichero, muchas propiedades si pueden cambiar: por ejemplo, las fechas de acceso y modificación pasan a reflejar el momento de la copia o el propietario em la copia es aquel que la realiza. La opción -p (de preservar) intenta que las propiedades cambien lo menos posible. De este modo, se conservaran las fechas (excepto la de cambio) y, si la copia la hace el administrador, se preservará incluso el propietario:

$ touch -t 201109301130 f1
$ ls -l f1
-rw-r--r-- 1 usuario usuario 0 sep 30  2011 f1
$ cp f1 fc.normal
$ cp -p f1 fc.preservado
$ ls -l f*
-rw-r--r-- 1 usuario usuario 0 sep 30  2011 f1
-rw-r--r-- 1 usuario usuario 0 oct 16 10:00 fc.normal
-rw-r--r-- 1 usuario usuario 0 sep 30  2011 fc.preservado

En principio, cp copia ficheros, pero es posible copiar también directorios añadiendo la opción -r.

En particular, cuando se quiere hacer una copia exacta de un directorio con todo lo que contiene, preservando sus propiedades, es conveniente usar la opción -a, que incluye -p2 y -r.

$ cp -l f1 f1.alternativo
$ ls -l f1*
-rw-r--r-- 2 usuario usuario 0 sep 30  2011 f1
-rw-r--r-- 2 usuario usuario 0 sep 30  2011 f1.alternativo

Para entender el concepto de enlace duro obsérve el siguiente gráfico:

Concepto de enlace duro y simbólico

Supongamos que el recuadro grande representa todo el espacio físico de un sistema de ficheros. Dentro de este sistema de ficheros, se encuentra un fichero que se encuentra referencia a través de dos nombres (nombre1 y nombre2). Al crear un enlace duro lo que estamos haciendo es crear una nueva referencia a partir de una que ya existía. En el sistema de ficheros, no hay dos ficheros, sino un único fichero con dos nombres. La limitación es que ambas referencias sólo pueden existir dentro del mismo sistema de ficheros. Por ese motivo, si intentamos crear un enlace duro en otro sistema de ficheros, obtendremos un error. Supongamos que /home y /tmp están en distinto sistema de ficheros:

$ cd
$ touch f1
$ cd /tmp
$ cp -l ~/f1 .
cp: crea el enlace duro './f1' a '/home/josem/f1': Enlace cruzado entre dispositivos no permitido

No pueden hacerse enlaces duros de directorios. Obsérvese, además, que ningún nombre es más importante que otro: el fichero se borrará definitivamente, mientras exista alguna referencia a él. Dicho de otro modo, no hay ningún nombre (referencia) original al que estén subordinados el resto de nombres.

ln

Una alternativa para crear enlaces es ln:

$ touch nombre1
$ ln nombre1 nombre2
$ ls -l nombre*
-rw-r--r-- 2 usuario usuario 0 oct 16 13:13 nombre1
-rw-r--r-- 2 usuario usuario 0 oct 16 13:13 nombre2
$ ln -s nombre1 simbolico
$ ls -l simbolico
lrwxrwxrwx 1 usuario usuario 7 oct 16 13:21 simbolico -> nombre1

Hay varias diferencias con el otro tipo de enlace:

  1. Existen dos ficheros distintos: el fichero original y el enlace simbólico.

  2. Los enlaces simbólicos se pueden hacer entre distintos sistemas de ficheros.

  3. Pueden enlazarse directorios.

  4. Si se borra el fichero original, el enlace simbólico permanecerá, pero apuntando a algo que ya no existe y, por tanto, deja de tener sentido:

    $ rm nombre1
    $ ls -l
    lrwxrwxrwx 1 usuario usuario 7 oct 16 13:21 simbolico -> nombre1
    $ cat simbolico
    cat: simbolico: No existe el fichero o el directorio
    

Advertencia

Ha de tenerse cuidado al crear enlaces simbólicos cuando se usa una ruta relativa para expresar el fichero apuntado. Esta ruta relativa no se expresa respecto al directorio de trabajo, sino respecto al propio enlace simbólico. Retomando el ejemplo anterior:

$ cd
$ touch nombre1
$ cd /tmp
$ ln -s nombre1 ~/simbolico

Como se quiere usar una ruta relativa, nombre1 debe extresarse respecto al directorio donde se desea crear simbólico. Por tanto la ruta relativa consiste en escribir simplemente el nombre, aunque nuestro dikrectorio de trabajo sea /tmp y no el directorio personal.

Nota

Aunque sólo se pueden crear enlaces duros con ficheros, es lícito con cp copiar directorios con todo su contenido, usar la opción -l. En ese caso, cp creara un nuevo directorio al copiar el directorio y enlaces duros al copiar los ficheros.

readlink

Resuelve el enlace simbólico devolviendo el fichero al que apunta:

$ touch soy.un.fichero
$ ln -s soy.un.fichero soy.un.enlace.simbolico
$ readlink soy.un.enlace.simbolico
soy.un.fichero

Si se usa como argumento un fichero que no es un enlace simbólico, no imprimirá nada y devolverá un error a la shell.

Si el enlace simbólico apunta a otro enlace simbólico se devolverá este segundo enlace, con lo que no se sabrá a ciencia cierta cuál es el fichero al que se está apuntando en realidad. Para ello existe la opción -f que, además, siempre muestra la ruta absoluta:

$ ln -s soy.un.enlace.simbolico soy.otro.enlace
$ readlink soy.otro.enlace
soy.un.enlace.simbolico
$ readlink -f soy.otro.enlace
/home/usuario/soy.un.fichero

En este caso, tiene sentido usar como argumento cualquier fichero: no se generará un error, sino que se mostrará su ruta absoluta:

$ readlink -f soy.un.fichero
/home/usuario/soy.un.fichero

Cuando se usa una interfaz de texto, gran parte de los ficheros que se manejan son de texto, así que una de las herramientas más socorridas son los editores de texto. En cualquier sistema unix suele venir de serie vi o su primo de Zumosol vim, pero los neófitos suelen preferir nano, un clónico mejorado del antiguo editor pico. También es muy usado emacs.

2.2.2.3. Ejercicios sobre manipulación de archivos

Nota

Especifique convenientemente si usa el administrador o no.

  1. Lleve a cabo las siguienes tareas:

    1. Crear un fichero vacío llamado estoy.en.blanco dentro del directorio personal del usuario.

    2. Hacer una copia de este fichero en el directorio temporal con nombre sigo.en.blanco. Use rutas relativas tanto para el origen como para el destino.

    3. Hacer un enlace duro en el directorio temporal conservando el nombre. ¿Es posible en el sistema en que está trabajando? ¿Por qué?

    4. Crear la siguiente estructura de directorios:

      /tmp
        +---- DIR1
        |      +------ DIR11
        |      +------ DIR12
        +---- DIR2
        +---- DIR3
          +------ DIR31
          +------ DIR32
          +------ DIR33
      
    5. Cambiar al directorio temporal usando ruta absoluta.

    6. Mover sigo.en.blanco al directorio DIR33.

    7. Manteniendo el mismo nombre, hacer un enlace simbólico a sigo.en.blanco en el directorio personal

    8. Mover el directorio DIR33 dentro de DIR12.

    9. Copiar todo el árbol que cuelga de DIR1 dentro de DIR32.

    10. Borrar todo lo que se ha hecho.

  2. Haga esta otra tanda de ejercicios, sabiendo que comienza a hacerlos estando en su directorio personal:

    1. Consulte el contenido de /usr/sbin usando ruta relativa.

    2. Encuentre los ficheros de extensión .txt.gz contenidos dentro de la parte del árbol de directorios que cualga de /usr/share/doc.

    3. Entre en el directorio temporal usando ruta relativa.

    4. Consulte cuáles son los permisos del directorio raíz. Utilice ruta relativa y dos órdenes distintas.

    5. Cree la siguiente estructura de archivos (vacíos) y directorios:

      + /tmp
          +--- dirA
          |     +-- fichero1.txt
          |     +-- dirAA
          |     |     +-- fichero2.txt
          |     |     +-- fichero3.txt
          |     |
          |     +-- dirAB
          |     +-- dirAC
          |           +-- fichero4.txt
          +--- dirB
      
    6. Mueva dirAA dentro de dirB.

    7. Buscar todos los ficheros de texto plano (extensión .txt) contenidos en el directorio temporal.

    #, Hacer en enlace duro dentro de dirAB del archivo

    fichero3.txt.

    1. Hacer un enlace simbólico en dirB de fichero1.txt y llamar a dicho enlace unfichero.txt.

    2. Borrar todo lo creado.

Notas al pie

1

En realidad, -p no preserva algunas propiedades como los atributos extendidos. Usar -a implica, más bien, --preserve=all.

2

which no busca todos los programas que hay en nuestro árbol de directorios, ya que estos pueden encontrarse en cualquier lugar arbitrario. Véase la variable PATH, para saber más al respecto.