Content from Introducción al shell
Última actualización: 2025-04-03 | Mejora esta página
Hoja de ruta
Preguntas
- ¿Qué es un intérprete de comandos y por qué debería utilizarlo?
Objetivos
- Explicar cómo se relaciona el shell con el teclado, la pantalla, el sistema operativo y los programas de los usuarios.
- Explicar cuándo y por qué deben utilizarse interfaces de línea de comandos en lugar de interfaces gráficas.
Antecedentes
Los humanos y los ordenadores suelen interactuar de muchas formas diferentes, como a través de un teclado y un ratón, interfaces de pantalla táctil o utilizando sistemas de reconocimiento de voz. La forma más utilizada para interactuar con los ordenadores personales se denomina interfaz gráfica de usuario (GUI). Con una GUI, damos instrucciones haciendo clic con el ratón y utilizando interacciones basadas en menús.
Aunque la ayuda visual de una interfaz gráfica de usuario hace que el aprendizaje sea intuitivo, esta forma de dar instrucciones a un ordenador es muy poco escalable. Imagine la siguiente tarea: para una búsqueda bibliográfica, tiene que copiar la tercera línea de mil archivos de texto en mil directorios diferentes y pegarla en un único archivo. Si utilizara una interfaz gráfica de usuario, no sólo pasaría varias horas haciendo clic en su escritorio, sino que además podría cometer un error en el proceso de realización de esta tarea repetitiva. Aquí es donde aprovechamos las ventajas del shell de Unix. El shell de Unix es tanto una interfaz de línea de comandos (CLI) como un lenguaje de secuencias de comandos, que permite realizar estas tareas repetitivas de forma automática y rápida. Con los comandos adecuados, el shell puede repetir tareas con o sin alguna modificación tantas veces como queramos. Usando el shell, la tarea del ejemplo de la literatura puede realizarse en segundos.
El Shell
El shell es un programa en el que los usuarios pueden escribir comandos. Con el shell, es posible invocar programas complicados como el software de modelado climático o comandos sencillos que crean un directorio vacío con una sola línea de código. El intérprete de comandos Unix más popular es Bash (Bourne Again SHell, así llamado porque deriva de un intérprete de comandos escrito por Stephen Bourne). Bash es el shell por defecto en la mayoría de las implementaciones modernas de Unix y en la mayoría de los paquetes que proporcionan herramientas tipo Unix para Windows. Ten en cuenta que ‘Git Bash’ es un software que permite a los usuarios de Windows utilizar una interfaz similar a Bash cuando interactúan con Git.
Utilizar el shell requiere cierto esfuerzo y tiempo de aprendizaje. Mientras que una interfaz gráfica de usuario te presenta opciones para seleccionar, las opciones de la CLI no se te presentan automáticamente, por lo que deberás aprender algunos comandos como si se tratara de vocabulario nuevo en un idioma que estás estudiando. Sin embargo, a diferencia de un idioma hablado, un pequeño número de “palabras” (es decir, comandos) te lleva un largo camino, y vamos a cubrir esos pocos esenciales hoy.
La gramática de un shell permite combinar herramientas existentes en potentes pipelines y manejar grandes volúmenes de datos de forma automática. Las secuencias de comandos pueden escribirse en un script, lo que mejora la reproducibilidad de los flujos de trabajo.
Además, la línea de comandos suele ser la forma más sencilla de interactuar con máquinas remotas y superordenadores. La familiaridad con el shell es casi esencial para ejecutar una variedad de herramientas y recursos especializados, incluidos los sistemas informáticos de alto rendimiento. A medida que los clústeres y los sistemas de computación en nube se hacen más populares para el procesamiento de datos científicos, ser capaz de interactuar con el intérprete de comandos se está convirtiendo en una habilidad necesaria. Podemos basarnos en las habilidades de la línea de comandos que se tratan aquí para abordar una amplia gama de cuestiones científicas y retos computacionales.
Empecemos.
Cuando se abre el intérprete de comandos por primera vez, aparece un prompt, que indica que el intérprete está esperando una entrada.
El shell suele utilizar $
como prompt, pero puede
utilizar un símbolo diferente. En los ejemplos de esta lección,
mostraremos el prompt como $
. Lo más importante, no
escribas el prompt cuando escribas comandos. Sólo escriba el
comando que sigue al prompt. Esta regla se aplica tanto en estas
lecciones como en lecciones de otras fuentes. También tenga en cuenta
que después de escribir un comando, tiene que pulsar la tecla
Enter para ejecutarlo.
El prompt va seguido de un cursor de texto, un carácter que indica la posición en la que aparecerá lo que escribas. El cursor suele ser un bloque parpadeante o sólido, pero también puede ser un guión bajo o una tubería. Es posible que lo hayas visto en un programa editor de texto, por ejemplo.
Ten en cuenta que tu prompt puede tener un aspecto algo diferente. En
particular, los entornos de shell más populares ponen por defecto su
nombre de usuario y el nombre del host antes de $
. Un
prompt de este tipo podría tener, por ejemplo, el aspecto siguiente
El prompt puede incluir incluso más que esto. No se preocupe si su
prompt no es sólo un corto $
. Esta lección no depende de
esta información adicional y tampoco debería ser un obstáculo. El único
elemento importante es el carácter $
y veremos más adelante
por qué.
Así que vamos a probar nuestro primer comando, ls
, que
es la abreviatura de listado. Este comando listará el contenido del
directorio actual:
SALIDA
Desktop Downloads Movies Pictures
Documents Library Music Public
Comando no encontrado
Nelle’s Pipeline: Un problema típico
Nelle Nemo, bióloga marina, acaba de regresar de un estudio de seis
meses en el Giro del
Pacífico Norte, donde ha estado tomando muestras de vida marina
gelatinosa en el Gran
Parche de Basura del Pacífico. Tiene 1520 muestras que ha pasado por
una máquina de ensayo para medir la abundancia relativa de 300
proteínas. Tiene que pasar estos 1520 archivos por un programa
imaginario llamado goostats.sh
. Además de esta ingente
tarea, tiene que escribir los resultados antes de fin de mes, para que
su artículo pueda aparecer en un número especial de Aquatic Goo
Letters.
Si Nelle elige ejecutar goostats.sh
a mano utilizando
una GUI, tendrá que seleccionar y abrir un archivo 1520 veces. Si
goostats.sh
tarda 30 segundos en ejecutar cada archivo,
todo el proceso requerirá más de 12 horas de la atención de Nelle. Con
el intérprete de comandos, Nelle puede asignar a su ordenador esta tarea
mundana mientras se concentra en escribir su trabajo.
Las próximas lecciones explorarán las formas en que Nelle puede
conseguirlo. Más concretamente, las lecciones explican cómo puede
utilizar un intérprete de comandos para ejecutar el programa
goostats.sh
, utilizando bucles para automatizar los pasos
repetitivos de introducción de nombres de archivo, de modo que su
ordenador pueda trabajar mientras ella escribe su trabajo.
Como ventaja adicional, una vez que haya creado un canal de procesamiento, podrá volver a utilizarlo siempre que recopile más datos.
Para realizar su tarea, Nelle necesita saber cómo:
- navegar a un archivo/directorio
- crear un archivo/directorio
- comprobar la longitud de un archivo
- encadenar comandos
- recuperar un conjunto de archivos
- iterar sobre archivos
- ejecutar un script de shell que contenga su pipeline
Puntos Clave
- Un shell es un programa cuyo propósito principal es leer comandos y ejecutar otros programas.
- Esta lección utiliza Bash, el shell por defecto en muchas implementaciones de Unix.
- Los programas pueden ejecutarse en Bash introduciendo comandos en el prompt de la línea de comandos.
- Las principales ventajas del intérprete de comandos son su elevada relación entre acciones y pulsaciones, su capacidad para automatizar tareas repetitivas y su capacidad para acceder a máquinas en red.
- Un reto importante a la hora de utilizar el shell puede ser saber qué comandos hay que ejecutar y cómo ejecutarlos.
Content from Navegación por archivos y directorios
Última actualización: 2025-04-03 | Mejora esta página
Hoja de ruta
Preguntas
- ¿Cómo puedo moverme por mi ordenador?
- ¿Cómo puedo ver qué ficheros y directorios tengo?
- ¿Cómo puedo especificar la ubicación de un archivo o directorio en mi ordenador?
Objetivos
- Explique las similitudes y diferencias entre un fichero y un directorio.
- Convierte una ruta absoluta en relativa y viceversa.
- Construye rutas absolutas y relativas que identifican archivos y directorios específicos.
- Usar opciones y argumentos para cambiar el comportamiento de un comando del shell.
- Demuestre el uso del tabulador y explique sus ventajas.
La parte del sistema operativo responsable de la gestión de ficheros y directorios se denomina sistema de ficheros. Organiza nuestros datos en ficheros, que contienen información, y directorios (también llamados “carpetas”), que contienen ficheros u otros directorios.
Hay varios comandos que se usan frecuentemente para crear, inspeccionar, renombrar y borrar archivos y directorios. Para empezar a explorarlos, iremos a nuestra ventana de shell abierta.
Primero, averigüemos dónde estamos ejecutando un comando llamado
pwd
(que significa ‘imprimir directorio de trabajo’). Los
directorios son como lugares - en cualquier momento mientras
usamos el shell, estamos exactamente en un lugar llamado nuestro
directorio de trabajo actual. La mayoría de los
comandos leen y escriben archivos en el directorio de trabajo actual, es
decir, “aquí”, por lo que es importante saber dónde nos encontramos
antes de ejecutar un comando.pwd
te muestra dónde
estás:
SALIDA
/Users/nelle
Aquí, la respuesta del ordenador es /Users/nelle
, que es
el directorio home de Nelle:
Variación del directorio de inicio
La ruta del directorio de inicio tendrá un aspecto diferente en los
distintos sistemas operativos. En Linux, puede parecerse a
/home/nelle
, y en Windows, será similar a
C:\Documents and Settings\nelle
o
C:\Users\nelle
. (Tenga en cuenta que puede tener un aspecto
ligeramente diferente para las distintas versiones de Windows.) En
futuros ejemplos, hemos utilizado la salida de Mac por defecto - la
salida de Linux y Windows puede diferir ligeramente, pero en general
debería ser similar.
También supondremos que su comando pwd
devuelve el
directorio personal de su usuario. Si pwd
devuelve algo
diferente, puede que necesite navegar hasta allí usando cd
o algunos comandos de esta lección no funcionarán como están escritos.
Ver Explorando otros
directorios para más detalles sobre el comando cd
.
Para entender qué es un “directorio personal”, veamos cómo está organizado el sistema de ficheros en su conjunto. En este ejemplo, ilustraremos el sistema de archivos del ordenador de nuestra científica Nelle. Después de esta ilustración, aprenderás comandos para explorar tu propio sistema de archivos, que estará construido de forma similar, pero no será exactamente idéntico.
En el ordenador de Nelle, el sistema de ficheros tiene el siguiente aspecto:
El sistema de ficheros parece un árbol al revés. El directorio
superior es el directorio raíz que contiene todo lo
demás. Nos referimos a él utilizando un carácter de barra oblicua,
/
, por sí solo; este carácter es la barra oblicua inicial
en /Users/nelle
.
Dentro de ese directorio hay otros directorios: bin
(que
es donde se almacenan algunos programas incorporados), data
(para archivos de datos varios), Users
(donde se encuentran
los directorios personales de los usuarios), tmp
(para
archivos temporales que no necesitan almacenarse a largo plazo),
etc.
Sabemos que nuestro directorio de trabajo actual
/Users/nelle
está almacenado dentro de /Users
porque /Users
es la primera parte de su nombre. Del mismo
modo, sabemos que /Users
está dentro del directorio raíz
/
porque su nombre empieza por /
.
Barra inclinada
Tenga en cuenta que el carácter /
tiene dos
significados. Cuando aparece al principio del nombre de un fichero o
directorio, se refiere al directorio raíz. Cuando aparece
dentro de una ruta, es sólo un separador.
Debajo de /Users
, encontramos un directorio para cada
usuario con una cuenta en la máquina de Nelle, sus colegas
imhotep y larry.
Los ficheros del usuario imhotep se almacenan en
/Users/imhotep
, los del usuario larry en
/Users/larry
, y los de Nelle en /Users/nelle
.
Nelle es el usuario de nuestros ejemplos; por lo tanto, tenemos
/Users/nelle
como nuestro directorio personal. Normalmente,
cuando abres un nuevo símbolo del sistema, estarás en tu directorio de
inicio para empezar.
Ahora vamos a aprender el comando que nos permitirá ver el contenido
de nuestro propio sistema de ficheros. Podemos ver lo que hay en nuestro
directorio home ejecutando ls
:
SALIDA
Applications Documents Library Music Public
Desktop Downloads Movies Pictures
(De nuevo, sus resultados pueden ser ligeramente diferentes dependiendo de su sistema operativo y de cómo haya personalizado su sistema de ficheros)
ls
imprime los nombres de los ficheros y directorios del
directorio actual. Podemos hacer que su salida sea más comprensible
utilizando la opción -F
que indica a
ls
que clasifique la salida añadiendo un marcador a los
nombres de archivos y directorios para indicar cuáles son:
- un
/
al final indica que se trata de un directorio -
@
indica un enlace -
*
indica un ejecutable
Dependiendo de la configuración por defecto de su shell, ésta también puede utilizar colores para indicar si cada entrada es un fichero o un directorio.
SALIDA
Applications/ Documents/ Library/ Music/ Public/
Desktop/ Downloads/ Movies/ Pictures/
Aquí, podemos ver que el directorio home contiene sólo sub-directorios. Cualquier nombre en la salida que no tenga un símbolo de clasificación son archivos en el directorio de trabajo actual.
Borrando tu terminal
Si su pantalla está demasiado llena, puede limpiar su terminal
utilizando el comando clear
. Todavía puedes acceder a
comandos anteriores utilizando ↑ y ↓ para moverte
línea a línea, o desplazándote por tu terminal.
Obtener ayuda
ls
tiene muchas otras opciones. Hay dos
formas comunes de averiguar cómo usar un comando y qué opciones acepta —
dependiendo de tu entorno, puede que sólo una de estas formas
funcione:
- Podemos pasar una opción
--help
a cualquier comando (disponible en Linux y Git Bash), por ejemplo:
- Podemos leer su manual con
man
(disponible en Linux y macOS):
A continuación describiremos ambas formas.
Ayuda para comandos incorporados
Algunos comandos están integrados en el shell Bash, en lugar de
existir como programas separados en el sistema de ficheros. Un ejemplo
es el comando cd
(cambiar directorio). Si recibes un
mensaje como No manual entry for cd
, prueba con
help cd
en su lugar. El comando help
es la
forma de obtener información de uso de Bash
built-ins.
La opción --help
La mayoría de los comandos bash y programas que la gente ha escrito
para ser ejecutados desde bash, soportan una opción --help
que muestra más información sobre cómo usar el comando o programa.
SALIDA
Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically if neither -cftuvSUX nor --sort is specified.
Mandatory arguments to long options are mandatory for short options, too.
-a, --all do not ignore entries starting with .
-A, --almost-all do not list implied . and ..
--author with -l, print the author of each file
-b, --escape print C-style escapes for nongraphic characters
--block-size=SIZE scale sizes by SIZE before printing them; e.g.,
'--block-size=M' prints sizes in units of
1,048,576 bytes; see SIZE format below
-B, --ignore-backups do not list implied entries ending with ~
-c with -lt: sort by, and show, ctime (time of last
modification of file status information);
with -l: show ctime and sort by name;
otherwise: sort by ctime, newest first
-C list entries by columns
--color[=WHEN] colorize the output; WHEN can be 'always' (default
if omitted), 'auto', or 'never'; more info below
-d, --directory list directories themselves, not their contents
-D, --dired generate output designed for Emacs' dired mode
-f do not sort, enable -aU, disable -ls --color
-F, --classify append indicator (one of */=>@|) to entries
... ... ...
Cuándo usar opciones cortas o largas
Cuando las opciones existen como opciones cortas y largas:
- Utilice la opción abreviada cuando escriba comandos directamente en el intérprete de comandos para minimizar las pulsaciones de teclas y realizar su tarea más rápidamente.
- Utilice la opción larga en los scripts para proporcionar claridad. Se leerá muchas veces y se escribirá una vez.
El comando man
La otra forma de conocer ls
es escribir
Este comando convertirá su terminal en una página con una descripción
del comando ls
y sus opciones.
Para navegar por las páginas man
, puede utilizar
↑ y ↓ para desplazarse línea a línea, o pruebe con
b y Barra espaciadora para saltar hacia arriba y
hacia abajo una página completa. Para buscar un carácter o palabra en
las páginas man
, utilice / seguido del carácter
o palabra que busca. A veces, una búsqueda dará como resultado varios
resultados. Si es así, puede desplazarse entre los resultados utilizando
N (para avanzar) y Mayús+N (para
retroceder).
Para salir de las páginas man
, pulse
q.
Páginas del manual en la web
Por supuesto, hay una tercera forma de acceder a la ayuda para
comandos: buscar en internet a través de su navegador web. Cuando
utilices la búsqueda en Internet, incluir la frase
unix man page
en tu consulta de búsqueda te ayudará a
encontrar resultados relevantes.
GNU proporciona enlaces a sus manuales incluyendo el núcleo de utilidades GNU, que cubre muchos comandos introducidos en esta lección.
Explorando más opciones ls
También puede utilizar dos opciones al mismo tiempo. ¿Qué hace el
comando ls
cuando se usa con la opción -l
? ¿Y
si utilizas tanto la opción -l
como la opción
-h
?
Parte de su salida es sobre propiedades que no cubrimos en esta lección (como permisos y propiedad de archivos), pero el resto debería ser útil de todos modos.
La opción -l
hace que ls
utilice un formato
de listado largo, mostrando no sólo los nombres de los
ficheros/directorios sino también información adicional, como el tamaño
del fichero y la hora de su última modificación. Si utiliza tanto la
opción -h
como la opción -l
, esto hace que el
tamaño del fichero sea ‘hman readable’, es decir,
mostrando algo como 5.3K
en lugar de 5369
.
Listado en orden cronológico inverso
Por defecto, ls
lista los contenidos de un directorio en
orden alfabético por nombre. El comando ls -t
lista los
elementos por la hora de la última modificación en lugar de
alfabéticamente. El comando ls -r
lista los contenidos de
un directorio en orden inverso. ¿Qué fichero se muestra en último lugar
cuando se combinan las opciones -t
y -r
?
Sugerencia: Puede que necesites usar la opción -l
para ver
las fechas de los últimos cambios.
El archivo modificado más recientemente aparece en último lugar
cuando se utiliza -rt
. Esto puede ser muy útil para
encontrar las ediciones más recientes o comprobar si se ha escrito un
nuevo archivo de salida.
Explorando Otros Directorios
No sólo podemos utilizar ls
en el directorio de trabajo
actual, sino que también podemos utilizarlo para listar el contenido de
un directorio diferente. Echemos un vistazo a nuestro directorio
Desktop
ejecutando ls -F Desktop
, es decir, el
comando ls
con la opción -F
y
el [argumento][Argumentos] Desktop
. El
argumento Desktop
indica a ls
que queremos un
listado de algo distinto a nuestro directorio de trabajo actual:
SALIDA
shell-lesson-data/
Tenga en cuenta que si un directorio llamado Desktop
no
existe en su directorio de trabajo actual, este comando devolverá un
error. Típicamente, un directorio Desktop
existe en su
directorio home, que asumimos es el directorio de trabajo actual de su
shell bash.
Su salida debería ser una lista de todos los archivos y
subdirectorios en su directorio de Escritorio, incluyendo el directorio
shell-lesson-data
que descargó en el setup para esta lección. (En la mayoría de los
sistemas, el contenido del directorio Desktop
en el
intérprete de comandos se mostrará como iconos en una interfaz gráfica
de usuario detrás de todas las ventanas abiertas. Compruebe si éste es
su caso)
Organizar las cosas jerárquicamente nos ayuda a llevar un control de nuestro trabajo. Aunque es posible poner cientos de archivos en nuestro directorio personal, igual que es posible apilar cientos de papeles impresos en nuestro escritorio, es mucho más fácil encontrar las cosas cuando se han organizado en subdirectorios con nombres sensatos.
Ahora que sabemos que el directorio shell-lesson-data
se
encuentra en nuestro directorio Escritorio, podemos hacer dos cosas.
Primero, usando la misma estrategia que antes, podemos mirar su
contenido pasando un nombre de directorio a ls
:
SALIDA
exercise-data/ north-pacific-gyre/
En segundo lugar, podemos cambiar nuestra ubicación a un directorio diferente, por lo que ya no estamos ubicados en nuestro directorio raíz.
El comando para cambiar de ubicación es cd
seguido de un
nombre de directorio para cambiar nuestro directorio de
trabajo.cd
significa “cambiar directorio”, lo que es un
poco engañoso. El comando no cambia el directorio; cambia el directorio
de trabajo actual del shell. En otras palabras, cambia la configuración
de la shell para saber en qué directorio estamos. El comando
cd
es similar a hacer doble clic en una carpeta en una
interfaz gráfica para entrar en esa carpeta.
Digamos que queremos movernos al directorio
exercise-data
que vimos anteriormente. Podemos utilizar la
siguiente serie de comandos para llegar allí:
Estos comandos nos moverán desde nuestro directorio de inicio a
nuestro directorio de Escritorio, luego al directorio
shell-lesson-data
, luego al directorio
exercise-data
. Notarás que cd
no imprime nada.
Esto es normal. Muchos comandos del shell no muestran nada en pantalla
cuando se ejecutan correctamente. Pero si ejecutamos pwd
después, podemos ver que ahora estamos en
/Users/nelle/Desktop/shell-lesson-data/exercise-data
.
Si ahora ejecutamos ls -F
sin argumentos, nos muestra el
contenido de
/Users/nelle/Desktop/shell-lesson-data/exercise-data
,
porque ahí es donde estamos ahora:
SALIDA
/Users/nelle/Desktop/shell-lesson-data/exercise-data
SALIDA
alkanes/ animal-counts/ creatures/ numbers.txt writing/
Ya sabemos cómo bajar por el árbol de directorios (es decir, cómo entrar en un subdirectorio), pero ¿cómo subimos (es decir, cómo salimos de un directorio y entramos en su directorio padre)? Podríamos intentar lo siguiente:
ERROR
-bash: cd: shell-lesson-data: No such file or directory
¡Pero obtenemos un error! ¿A qué se debe?
Con nuestros métodos hasta ahora, cd
sólo puede ver
subdirectorios dentro de su directorio actual. Hay diferentes maneras de
ver los directorios por encima de su ubicación actual, vamos a empezar
con el más simple.
Hay un atajo en el shell para subir un nivel de directorio. Funciona de la siguiente manera:
..
es un nombre de directorio especial que significa “el
directorio que contiene a éste”, o más sucintamente, el
padre del directorio actual. Efectivamente, si
ejecutamos pwd
después de ejecutar cd ..
,
volvemos a estar en
/Users/nelle/Desktop/shell-lesson-data
:
SALIDA
/Users/nelle/Desktop/shell-lesson-data
El directorio especial ..
no suele aparecer cuando
ejecutamos ls
. Si queremos mostrarlo, podemos añadir la
opción -a
a ls -F
:
SALIDA
./ ../ exercise-data/ north-pacific-gyre/
-a
significa ‘mostrar todo’ (incluyendo ficheros
ocultos); fuerza a ls
a mostrarnos nombres de ficheros y
directorios que empiecen por .
, como ..
(que,
si estamos en /Users/nelle
, se refiere al directorio
/Users
). Como puedes ver, también muestra otro directorio
especial que se llama simplemente .
, que significa ‘el
directorio de trabajo actual’. Puede parecer redundante tener un nombre
para él, pero pronto le veremos algunos usos.
Tenga en cuenta que en la mayoría de las herramientas de línea de
comandos, varias opciones se pueden combinar con una sola -
y sin espacios entre las opciones; ls -F -a
es equivalente
a ls -Fa
.
Otros ficheros ocultos
Además de los directorios ocultos ..
y .
,
también puede ver un fichero llamado .bash_profile
. Este
archivo suele contener ajustes de configuración del shell. También puede
ver otros ficheros y directorios que empiezan por .
.
Normalmente son ficheros y directorios que se utilizan para configurar
diferentes programas en tu ordenador. El prefijo .
se
utiliza para evitar que estos ficheros de configuración saturen el
terminal cuando se utiliza un comando estándar ls
.
Estos tres comandos son los comandos básicos para navegar por el
sistema de ficheros de tu ordenador: pwd
, ls
,
y cd
. Exploremos algunas variaciones de estos comandos.
¿Qué ocurre si escribes cd
solo, sin dar un directorio?
¿Cómo puedes comprobar qué ha pasado? ¡pwd
nos da la
respuesta!
SALIDA
/Users/nelle
Resulta que cd
sin un argumento te devolverá a tu
directorio home, lo cual es genial si te has perdido en tu propio
sistema de ficheros.
Intentemos volver al directorio exercise-data
de antes.
La última vez, usamos tres comandos, pero en realidad podemos encadenar
la lista de directorios para movernos a exercise-data
en un
solo paso:
Comprueba que nos hemos movido al lugar correcto ejecutando
pwd
y ls -F
.
Si queremos subir un nivel desde el directorio de datos, podríamos
usar cd ..
. Pero hay otra forma de moverse a cualquier
directorio, independientemente de su ubicación actual.
Hasta ahora, al especificar nombres de directorio, o incluso una ruta
de directorio (como arriba), hemos estado utilizando rutas
relativas. Cuando se utiliza una ruta relativa con un comando
como ls
o cd
, se intenta encontrar esa
ubicación desde donde nos encontramos, en lugar de desde la raíz del
sistema de ficheros.
Sin embargo, es posible especificar la ruta absoluta
a un directorio incluyendo su ruta completa desde el directorio raíz,
que se indica mediante una barra oblicua inicial. La barra
/
indica al ordenador que siga la ruta desde la raíz del
sistema de ficheros, por lo que siempre se refiere exactamente a un
directorio, independientemente de dónde nos encontremos cuando
ejecutemos el comando.
Esto nos permite movernos a nuestro directorio
shell-lesson-data
desde cualquier parte del sistema de
ficheros (incluso desde dentro de exercise-data
). Para
encontrar la ruta absoluta que estamos buscando, podemos usar
pwd
y luego extraer el trozo que necesitamos mover a
shell-lesson-data
.
SALIDA
/Users/nelle/Desktop/shell-lesson-data/exercise-data
Ejecuta pwd
y ls -F
para asegurarte de que
estamos en el directorio que esperamos.
Dos atajos más
El shell interpreta que un carácter con tilde (~
) al
principio de una ruta significa “el directorio personal del usuario
actual”. Por ejemplo, si el directorio personal de Nelle es
/Users/nelle
, entonces ~/data
es equivalente a
/Users/nelle/data
. Esto sólo funciona si es el primer
carácter de la ruta; here/there/~/elsewhere
no es
here/there/Users/nelle/elsewhere
.
Otro atajo es el carácter -
(guión).cd
traducirá -
a el directorio anterior en el que
estaba, lo que es más rápido que tener que recordar, y luego
teclear, la ruta completa. Esta es una forma muy eficiente de
moverse hacia adelante y hacia atrás entre dos directorios – es
decir, si ejecuta cd -
dos veces, acabará de vuelta en el
directorio inicial.
La diferencia entre cd ..
y cd -
es que el
primero te lleva hacia arriba, mientras que el segundo te lleva
hacia atrás.
¡Pruébelo! Primero navegue a ~/Desktop/shell-lesson-data
(ya debería estar allí).
Luego cd
en el directorio
exercise-data/creatures
Ahora si ejecuta
verás que estás de vuelta en
~/Desktop/shell-lesson-data
. Ejecute cd -
de
nuevo y estará de vuelta en
~/Desktop/shell-lesson-data/exercise-data/creatures
Rutas absolutas vs relativas
Partiendo de /Users/nelle/data
, ¿cuál de los siguientes
comandos podría utilizar Nelle para navegar hasta su directorio
personal, que es /Users/nelle
?
cd .
cd /
cd /home/nelle
cd ../..
cd ~
cd home
cd ~/data/..
cd
cd ..
- No:
.
representa el directorio actual. - No:
/
representa el directorio raíz. - No: El directorio personal de Nelle es
/Users/nelle
. - No: este comando sube dos niveles, es decir, termina en
/Users
. - Sí:
~
representa el directorio personal del usuario, en este caso/Users/nelle
. - No: este comando navegaría a un directorio
home
en el directorio actual si existe. - Sí: innecesariamente complicado, pero correcto.
- Sí: acceso directo para volver al directorio personal del usuario.
- Sí: sube un nivel.
Resolución de la ruta relativa
Usando el siguiente diagrama del sistema de ficheros, si
pwd
muestra /Users/thing
, ¿qué mostrará
ls -F ../backup
?
../backup: No such file or directory
2012-12-01 2013-01-08 2013-01-27
2012-12-01/ 2013-01-08/ 2013-01-27/
original/ pnas_final/ pnas_sub/
- No: existe *un directorio
backup
en/Users
. - No: este es el contenido de
Users/thing/backup
, pero con..
, pedimos un nivel más arriba. - No: ver explicación anterior.
- Sí:
../backup/
se refiere a/Users/backup/
.
ls
Comprensión lectora
Usando el diagrama del sistema de ficheros de abajo, si
pwd
muestra /Users/backup
, y -r
le dice a ls
que muestre las cosas en orden inverso, qué
comando(s) resultará(n) en la siguiente salida:
SALIDA
pnas_sub/ pnas_final/ original/
ls pwd
ls -r -F
ls -r -F /Users/backup
- No:
pwd
no es el nombre de un directorio. - Sí:
ls
sin el argumento directorio lista los archivos y directorios en el directorio actual. - Sí: utiliza la ruta absoluta explícitamente.
Sintaxis General de un Comando Shell
Ya hemos visto comandos, opciones y argumentos, pero quizás sea útil formalizar alguna terminología.
Considere el siguiente comando como un ejemplo general de un comando, que diseccionaremos en sus partes componentes:
ls
es el comando, con una
opción -F
y un argumento
/
. Ya hemos visto opciones que empiezan con un guión
(-
), conocidas como opciones cortas, o con
dos guiones (--
), conocidas como opciones
largas. las [Opciones] cambian el comportamiento de un comando
y los [Argumentos] le indican al comando sobre qué operar (por ejemplo,
archivos y directorios). A veces, las opciones y los argumentos se
denominan parámetros. Un comando puede ser llamado con
más de una opción y más de un argumento, pero un comando no siempre
requiere un argumento o una opción.
Puede que a veces veas que se hace referencia a las opciones como interruptores o banderas, especialmente para opciones que no tienen argumento. En esta lección utilizaremos el término opción.
Cada parte está separada por espacios. Si omite el espacio entre
ls
y -F
el shell buscará un comando llamado
ls-F
, que no existe. Además, las mayúsculas pueden ser
importantes. Por ejemplo, ls -s
mostrará el tamaño de los
ficheros y directorios junto a los nombres, mientras que
ls -S
ordenará los ficheros y directorios por tamaño, como
se muestra a continuación:
SALIDA
total 28
4 animal-counts 4 creatures 12 numbers.txt 4 alkanes 4 writing
Tenga en cuenta que los tamaños devueltos por ls -s
están en bloques. Como éstos se definen de forma diferente para
los distintos sistemas operativos, es posible que no obtenga las mismas
cifras que en el ejemplo.
SALIDA
animal-counts creatures alkanes writing numbers.txt
Juntando todo esto, nuestro comando ls -F /
de arriba
nos da un listado de ficheros y directorios en el directorio raíz
/
. A continuación se muestra un ejemplo de la salida que
puede obtener del comando anterior:
SALIDA
Applications/ System/
Library/ Users/
Network/ Volumes/
Nelle’s Pipeline: Organización de ficheros
Sabiendo esto sobre archivos y directorios, Nelle está lista para organizar los archivos que la máquina de ensayo de proteínas creará.
Crea un directorio llamado north-pacific-gyre
(para
recordar de dónde proceden los datos), que contendrá los archivos de
datos de la máquina de ensayo y sus scripts de procesamiento de
datos.
Cada una de sus muestras físicas está etiquetada según la convención
de su laboratorio con un ID único de diez caracteres, como “NENE01729A”.
Este ID es el que utiliza en su registro de recogida para anotar la
ubicación, la hora, la profundidad y otras características de la
muestra, por lo que decide utilizarlo en el nombre de archivo de cada
fichero de datos. Como la salida de la máquina de ensayo es texto sin
formato, llamará a sus archivos NENE01729A.txt
,
NENE01812A.txt
, etcétera. Los 1520 ficheros irán en el
mismo directorio.
Ahora en su directorio actual shell-lesson-data
, Nelle
puede ver qué ficheros tiene usando el comando
Este comando es mucho para escribir, pero puede dejar que el shell haga la mayor parte del trabajo a través de lo que se llama completar pestañas. Si escribe:
y, a continuación, pulsa Tab (la tecla de tabulación de su teclado), el shell completa automáticamente el nombre del directorio por ella:
Pulsar Tab de nuevo no hace nada, ya que hay múltiples posibilidades; pulsando Tab dos veces aparece una lista de todos los archivos.
Si Nelle pulsa G y después Tab de nuevo, el intérprete de comandos añadirá “goo”, ya que todos los archivos que empiezan por “g” comparten los tres primeros caracteres “goo”.
Para ver todos esos archivos, puede pulsar Tab dos veces más.
Esto se llama completar pestañas, y lo veremos en muchas otras herramientas a medida que avancemos.
Puntos Clave
- El sistema de ficheros se encarga de gestionar la información del disco.
- La información se almacena en ficheros, que a su vez se almacenan en directorios (carpetas).
- Los directorios también pueden almacenar otros directorios, que entonces forman un árbol de directorios.
-
pwd
imprime el directorio de trabajo actual del usuario. -
ls [path]
imprime un listado de un fichero o directorio específico;ls
por sí solo lista el directorio de trabajo actual. -
cd [path]
cambia el directorio de trabajo actual. - La mayoría de los comandos toman opciones que comienzan con
-
. - Los nombres de directorio en una ruta se separan con
/
en Unix, pero\
en Windows. -
/
por sí solo es el directorio raíz de todo el sistema de ficheros. - Una ruta absoluta especifica una ubicación desde la raíz del sistema de ficheros.
- Una ruta relativa especifica una ubicación a partir de la ubicación actual.
-
.
por sí solo significa ‘el directorio actual’;..
significa ‘el directorio por encima del actual’.
Content from Trabajar con archivos y directorios
Última actualización: 2025-04-03 | Mejora esta página
Hoja de ruta
Preguntas
- ¿Cómo puedo crear, copiar y borrar archivos y directorios?
- ¿Cómo puedo editar los archivos?
Objetivos
- Eliminar, copiar y mover archivos y/o directorios especificados.
- Crea archivos en esa jerarquía utilizando un editor o copiando y renombrando archivos existentes.
- Crea una jerarquía de directorios que coincide con un diagrama dado.
Creando directorios
Ahora sabemos cómo explorar archivos y directorios, pero ¿cómo los creamos en primer lugar?
En este episodio aprenderemos a crear y mover archivos y directorios,
usando el directorio exercise-data/writing
como
ejemplo.
Primer paso: ver dónde estamos y qué tenemos ya
Deberíamos seguir en el directorio shell-lesson-data
del
Escritorio, lo que podemos comprobar utilizando:
SALIDA
/Users/nelle/Desktop/shell-lesson-data
A continuación nos moveremos al directorio
exercise-data/writing
y veremos qué contiene:
SALIDA
haiku.txt LittleWomen.txt
Crear un directorio
Vamos a crear un nuevo directorio llamado thesis
usando
el comando mkdir thesis
(que no tiene salida):
Como puede adivinar por su nombre, mkdir
significa
hacer directorio
. Como thesis
es una ruta
relativa (es decir, no tiene una barra al principio, como
/what/ever/thesis
), el nuevo directorio se crea en el
directorio de trabajo actual:
SALIDA
haiku.txt LittleWomen.txt thesis/
Como acabamos de crear el directorio thesis
, todavía no
hay nada en él:
Tenga en cuenta que mkdir
no se limita a crear
directorios individuales de uno en uno. La opción -p
permite a mkdir
crear un directorio con subdirectorios
anidados en una sola operación:
La opción -R
del comando ls
listará todos
los subdirectorios anidados dentro de un directorio. Usemos
ls -FR
para listar recursivamente la nueva jerarquía de
directorios que acabamos de crear en el directorio
project
:
SALIDA
../project/:
data/ results/
../project/data:
../project/results:
Dos formas de hacer lo mismo
Utilizar el shell para crear un directorio no es diferente de
utilizar un explorador de archivos. Si abre el directorio actual
utilizando el explorador gráfico de archivos de su sistema operativo, el
directorio thesis
también aparecerá allí. Aunque el shell y
el explorador de archivos son dos formas diferentes de interactuar con
los archivos, los archivos y directorios en sí son los mismos.
Buenos nombres para archivos y directorios
Los nombres complicados de archivos y directorios pueden hacerte la vida imposible cuando trabajas en la línea de comandos. Aquí te ofrecemos algunos consejos útiles para los nombres de tus archivos y directorios.
- No uses espacios.
Los espacios pueden hacer que un nombre tenga más sentido, pero dado
que los espacios se utilizan para separar argumentos en la línea de
órdenes, es mejor evitarlos en los nombres de ficheros y directorios.
Puede utilizar -
o _
en su lugar (por ejemplo,
north-pacific-gyre/
en lugar de
north pacific gyre/
). Para comprobarlo, pruebe a escribir
mkdir north pacific gyre
y vea qué directorio (¡o
directorios!) se crean al comprobarlo con ls -F
.
- No empiece el nombre con
-
(guión).
Los comandos tratan los nombres que empiezan por -
como
opciones.
- Utiliza letras, números,
.
(punto o ‘punto final’),-
(guión) y_
(guión bajo).
Muchos otros caracteres tienen significados especiales en la línea de comandos. Aprenderemos sobre algunos de ellos durante esta lección. Hay caracteres especiales que pueden hacer que su comando no funcione como se espera e incluso pueden provocar la pérdida de datos.
Si necesita referirse a nombres de archivos o directorios que tienen
espacios u otros caracteres especiales, debe rodear el nombre entre
[comillas] simples (https://www.gnu.org/software/bash/manual/html_node/Quoting.html)
(''
).
Crear un archivo de texto
Cambiemos nuestro directorio de trabajo a thesis
usando
cd
, luego ejecute un editor de texto llamado Nano para
crear un archivo llamado draft.txt
:
¿Qué Editor?
Cuando decimos “nano
es un editor de texto” nos
referimos realmente a “texto”. Sólo puede trabajar con datos de
caracteres planos, no con tablas, imágenes o cualquier otro medio
amigable. Lo utilizamos en los ejemplos porque es uno de los editores de
texto menos complejos. Sin embargo, debido a esta característica, puede
que no sea lo suficientemente potente o flexible para el trabajo que
necesites hacer después de este taller. En sistemas Unix (como Linux y
macOS), muchos programadores utilizan Emacs o Vim (ambos requieren más tiempo de
aprendizaje), o un editor gráfico como Gedit o VScode. En Windows, puede
utilizar Notepad++. Windows
también tiene un editor incorporado llamado notepad
que se
puede ejecutar desde la línea de comandos de la misma manera que
nano
para los propósitos de esta lección.
Independientemente del editor que utilices, necesitarás saber dónde busca y guarda los archivos. Si lo inicia desde el intérprete de comandos, (probablemente) utilizará su directorio de trabajo actual como ubicación predeterminada. Si utilizas el menú de inicio de tu ordenador, puede que quiera guardar los archivos en tu Escritorio o en el directorio Documentos. Puede cambiar esto navegando a otro directorio la primera vez que ‘Guarde como…’
Escribamos unas líneas de texto.

Una vez que estemos satisfechos con nuestro texto, podemos pulsar
Ctrl+O (pulsar la tecla Ctrl o
Control y, sin soltarla, pulsar la tecla O) para
escribir nuestros datos en el disco. Se nos pedirá que proporcionemos un
nombre para el archivo que contendrá nuestro texto. Pulse
Return para aceptar el valor predeterminado sugerido de
draft.txt
.
Una vez guardado nuestro archivo, podemos usar Ctrl+X para salir del editor y volver al shell.
Tecla Control, Ctrl o ^
La tecla Control también se llama tecla ‘Ctrl’. Hay varias formas de describir el uso de la tecla Control. Por ejemplo, puede ver una instrucción para pulsar la tecla Control y, mientras la mantiene pulsada, pulsar la tecla X, descrita como cualquiera de:
Control-X
Control+X
Ctrl-X
Ctrl+X
^X
C-x
En nano, en la parte inferior de la pantalla verás
^G Get Help ^O WriteOut
. Esto significa que puedes usar
Control-G
para obtener ayuda y Control-O
para
guardar tu archivo.
nano
no deja ninguna salida en la pantalla después de
salir, pero ls
muestra ahora que hemos creado un fichero
llamado draft.txt
:
SALIDA
draft.txt
Crear archivos de otra manera
Hemos visto cómo crear archivos de texto utilizando el editor
nano
. Ahora, prueba el siguiente comando:
¿Qué hizo el comando
touch
? Cuando miras tu directorio actual usando el explorador de archivos GUI, ¿aparece el archivo?Utilice
ls -l
para inspeccionar los archivos. ¿Qué tamaño tienemy_file.txt
?¿Cuándo podría querer crear un archivo de esta manera?
El comando
touch
genera un nuevo fichero llamadomy_file.txt
en tu directorio actual. Puede observar este archivo recién generado escribiendols
en la línea de comandos. también puede vermy_file.txt
en su explorador de archivos GUI.Al inspeccionar el fichero con
ls -l
, observe que el tamaño demy_file.txt
es de 0 bytes. En otras palabras, no contiene datos. Si abremy_file.txt
con su editor de texto, aparecerá en blanco.Algunos programas no generan archivos de salida por sí mismos, sino que requieren que ya se hayan generado archivos vacíos. Cuando el programa se ejecuta, busca un archivo existente para rellenarlo con su salida. El comando touch le permite generar eficientemente un archivo de texto en blanco para ser utilizado por tales programas.
Crear archivos de otra manera (continued)
¿Qué hay en un nombre?
Te habrás dado cuenta de que todos los archivos de Nelle se llaman
‘algo punto algo’, y en esta parte de la lección, siempre hemos
utilizado la extensión .txt
. Esto es sólo una convención;
podemos llamar a un archivo mythesis
o casi cualquier otra
cosa que queramos. Sin embargo, la mayoría de la gente utiliza nombres
de dos partes la mayor parte del tiempo para ayudarles (y a sus
programas) a distinguir diferentes tipos de ficheros. La segunda parte
de un nombre de este tipo se llama extensión de nombre de
fichero e indica qué tipo de datos contiene el fichero:
.txt
señala un fichero de texto plano, .pdf
indica un documento PDF, .cfg
es un fichero de
configuración lleno de parámetros para algún programa, .png
es una imagen PNG, etc.
Esto es sólo una convención, aunque importante. Los archivos sólo contienen bytes; depende de nosotros y de nuestros programas interpretar esos bytes de acuerdo con las reglas para archivos de texto plano, documentos PDF, archivos de configuración, imágenes, etcétera.
Nombrar una imagen PNG de una ballena como whale.mp3
no
la convierte mágicamente en una grabación del canto de una ballena,
aunque puede hacer que el sistema operativo asocie el fichero
con un programa reproductor de música. En este caso, si alguien hace
doble clic en whale.mp3
en un programa explorador de
archivos, el reproductor de música intentará automáticamente (y por
error) abrir el archivo whale.mp3
.
Moviendo ficheros y directorios
Volviendo al directorio
shell-lesson-data/exercise-data/writing
,
En nuestro directorio thesis
tenemos un fichero
draft.txt
que no es un nombre particularmente informativo,
así que cambiemos el nombre del fichero usando mv
, que es
la abreviatura de ‘move’:
El primer argumento dice mv
lo que estamos ‘moviendo’,
mientras que el segundo es a dónde va a ir. En este caso, estamos
moviendo thesis/draft.txt
a thesis/quotes.txt
,
lo que tiene el mismo efecto que renombrar el fichero. Efectivamente,
ls
nos muestra que thesis
contiene ahora un
fichero llamado quotes.txt
:
SALIDA
quotes.txt
Hay que tener cuidado al especificar el nombre del fichero de
destino, ya que mv
sobrescribirá silenciosamente cualquier
fichero existente con el mismo nombre, lo que podría provocar la pérdida
de datos. Por defecto, mv
no pedirá confirmación antes de
sobrescribir archivos. Sin embargo, una opción adicional,
mv -i
(o mv --interactive
), hará que
mv
solicite dicha confirmación.
Tenga en cuenta que mv
también funciona en
directorios.
Movamos quotes.txt
al directorio de trabajo actual.
Usaremos mv
una vez más, pero esta vez usaremos sólo el
nombre de un directorio como segundo argumento para decirle a
mv
que queremos mantener el nombre del fichero pero ponerlo
en un lugar nuevo. (En este caso, el nombre de directorio que utilizamos
es el nombre de directorio especial .
que hemos mencionado
antes.
El efecto es mover el fichero del directorio en el que estaba al
directorio de trabajo actual. ls
nos muestra ahora que
thesis
está vacío:
SALIDA
$
Alternativamente, podemos confirmar que el fichero
quotes.txt
ya no está presente en el directorio
thesis
intentando listarlo explícitamente:
ERROR
ls: cannot access 'thesis/quotes.txt': No such file or directory
ls
con un nombre de fichero o directorio como argumento
sólo muestra el fichero o directorio solicitado. Si el fichero dado como
argumento no existe, el shell devuelve un error como vimos
anteriormente. Podemos usar esto para ver que quotes.txt
está ahora presente en nuestro directorio actual:
SALIDA
quotes.txt
Moviendo archivos a una nueva carpeta
Después de ejecutar los siguientes comandos, Jamie se da cuenta de
que puso los archivos sucrose.dat
y
maltose.dat
en la carpeta equivocada. Los ficheros deberían
haberse colocado en la carpeta raw
.
BASH
$ ls -F
analyzed/ raw/
$ ls -F analyzed
fructose.dat glucose.dat maltose.dat sucrose.dat
$ cd analyzed
Rellene los espacios en blanco para mover estos archivos a la carpeta
raw/
(es decir, aquella en la que olvidó ponerlos)
Copiando ficheros y directorios
El comando cp
funciona de forma muy parecida a
mv
, excepto que copia un fichero en lugar de moverlo.
Podemos comprobar que ha hecho lo correcto utilizando ls
con dos rutas como argumentos — como la mayoría de los comandos Unix,
ls
puede recibir múltiples rutas a la vez:
SALIDA
quotes.txt thesis/quotations.txt
También podemos copiar un directorio y todo su contenido utilizando
la opción recursive
-r
, por ejemplo, para hacer una copia de seguridad de un
directorio:
Podemos comprobar el resultado listando el contenido de los
directorios thesis
y thesis_backup
:
SALIDA
thesis:
quotations.txt
thesis_backup:
quotations.txt
Es importante incluir la bandera -r
. Si quieres copiar
un directorio y omites esta opción verás un mensaje de que el directorio
ha sido omitido porque -r not specified
.
Renombrar Archivos
Supongamos que ha creado un archivo de texto plano en su directorio
actual para contener una lista de las pruebas estadísticas que
necesitará realizar para analizar sus datos, y lo ha llamado
statstics.txt
Después de crear y guardar este fichero te das cuenta de que has escrito mal el nombre Quieres corregir el error, ¿cuál de los siguientes comandos podrías utilizar para hacerlo?
cp statstics.txt statistics.txt
mv statstics.txt statistics.txt
mv statstics.txt .
cp statstics.txt .
- No. Aunque esto crearía un fichero con el nombre correcto, el fichero con el nombre incorrecto todavía existe en el directorio y tendría que ser borrado.
- Sí, esto funcionaría para cambiar el nombre del archivo.
- No, el punto(.) indica dónde mover el archivo, pero no proporciona un nuevo nombre de archivo; no se pueden crear nombres de archivo idénticos.
- No, el punto(.) indica dónde copiar el archivo, pero no proporciona un nuevo nombre de archivo; no se pueden crear nombres de archivo idénticos.
Mover y copiar
¿Cuál es la salida del comando de cierre ls
en la
secuencia que se muestra a continuación?
SALIDA
/Users/jamie/data
SALIDA
proteins.dat
BASH
$ mkdir recombined
$ mv proteins.dat recombined/
$ cp recombined/proteins.dat ../proteins-saved.dat
$ ls
proteins-saved.dat recombined
recombined
proteins.dat recombined
proteins-saved.dat
Empezamos en el directorio /Users/jamie/data
, y creamos
una nueva carpeta llamada recombined
. La segunda línea
mueve (mv
) el fichero proteins.dat
a la nueva
carpeta (recombined
). La tercera línea hace una copia del
fichero que acabamos de mover. La parte complicada aquí es dónde se ha
copiado el archivo. Recuerda que ..
significa ‘subir un
nivel’, así que el fichero copiado está ahora en
/Users/jamie
. Tenga en cuenta que ..
se
interpreta con respecto al directorio de trabajo actual,
no con respecto a la ubicación del fichero que se está
copiando. Así, lo único que se mostrará usando ls (en
/Users/jamie/data
) es la carpeta recombinada.
- No, véase la explicación anterior.
proteins-saved.dat
se encuentra en/Users/jamie
- Sí
- No, véase la explicación anterior.
proteins.dat
se encuentra en/Users/jamie/data/recombined
- No, véase la explicación anterior.
proteins-saved.dat
se encuentra en/Users/jamie
Eliminando ficheros y directorios
Volviendo al directorio
shell-lesson-data/exercise-data/writing
, vamos a ordenar
este directorio eliminando el archivo quotes.txt
que hemos
creado. El comando Unix que utilizaremos para ello es rm
(abreviatura de ‘remove’):
Podemos confirmar que el archivo se ha ido usando
ls
:
ERROR
ls: cannot access 'quotes.txt': No such file or directory
Borrar es para siempre
El shell de Unix no tiene una papelera de la que podamos recuperar los ficheros borrados (aunque la mayoría de las interfaces gráficas de Unix sí la tienen). En su lugar, cuando borramos archivos, éstos son desvinculados del sistema de archivos para que su espacio de almacenamiento en disco pueda ser reciclado. Existen herramientas para encontrar y recuperar archivos borrados, pero no hay garantía de que funcionen en una situación concreta, ya que el ordenador puede reciclar el espacio del archivo en el disco inmediatamente.
Usando rm
con seguridad
¿Qué ocurre cuando ejecutamos
rm -i thesis_backup/quotations.txt
? ¿Por qué querríamos
esta protección cuando usamos rm
?
SALIDA
rm: remove regular file 'thesis_backup/quotations.txt'? y
La opción -i
preguntará antes (de cada) borrado (use
Y para confirmar el borrado o N para mantener el
fichero). El shell Unix no tiene papelera, por lo que todos los archivos
eliminados desaparecerán para siempre. Utilizando la opción
-i
, tenemos la posibilidad de comprobar que estamos
borrando sólo los ficheros que queremos eliminar.
Si intentamos eliminar el directorio thesis
utilizando
rm thesis
, obtenemos un mensaje de error:
ERROR
rm: cannot remove 'thesis': Is a directory
Esto ocurre porque rm
por defecto sólo funciona en
ficheros, no en directorios.
rm
puede eliminar un directorio y todo su
contenido si usamos la opción recursiva -r
, y lo hará
sin ninguna petición de confirmación:
Dado que no hay forma de recuperar ficheros borrados usando el shell,
rm -r
debe usarse con mucha precaución (puedes
considerar añadir la opción interactiva rm -r -i
).
Operaciones con múltiples archivos y directorios
A menudo es necesario copiar o mover varios ficheros a la vez. Esto puede hacerse proporcionando una lista de nombres de ficheros individuales, o especificando un patrón de nombres utilizando comodines. Los comodines son caracteres especiales que se pueden utilizar para representar caracteres desconocidos o conjuntos de caracteres al navegar por el sistema de ficheros Unix.
Copia con varios nombres de archivo
Para este ejercicio, puede probar los comandos en el directorio
shell-lesson-data/exercise-data
.
En el siguiente ejemplo, ¿qué hace cp
cuando se le dan
varios nombres de archivo y un nombre de directorio?
En el ejemplo siguiente, ¿qué hace cp
cuando se le dan
tres o más nombres de archivo?
SALIDA
basilisk.dat minotaur.dat unicorn.dat
Si se da más de un nombre de fichero seguido de un nombre de
directorio (es decir, el directorio de destino debe ser el último
argumento), cp
copia los ficheros en el directorio
nombrado.
Si se le dan tres nombres de fichero, cp
lanza un error
como el siguiente, porque está esperando un nombre de directorio como
último argumento.
ERROR
cp: target 'basilisk.dat' is not a directory
Uso de comodines para acceder a varios archivos a la vez
Comodines
*
es un código comodín, que representa
otros cero o más caracteres. Consideremos el directorio
shell-lesson-data/exercise-data/alkanes
: *.pdb
representa ethane.pdb
, propane.pdb
y todos los
archivos que terminan en ‘.pdb’. En cambio, p*.pdb
sólo
representa a pentane.pdb
y propane.pdb
, porque
la p
de delante sólo puede representar nombres de fichero
que empiecen por la letra p
.
?
también es un comodín, pero representa exactamente un
carácter. Así, ?ethane.pdb
podría representar
methane.pdb
mientras que *ethane.pdb
representa tanto ethane.pdb
como
methane.pdb
.
Los comodines se pueden utilizar combinados entre sí. Por ejemplo,
???ane.pdb
indica tres caracteres seguidos de
ane.pdb
, dando
cubane.pdb ethane.pdb octane.pdb
.
Cuando el shell ve un comodín, lo expande para crear una lista de
nombres de archivo coincidentes antes de ejecutar el comando
precedente. Como excepción, si una expresión comodín no coincide con
ningún archivo, Bash pasará la expresión como argumento al comando tal
cual. Por ejemplo, teclear ls *.pdf
en el directorio
alkanes
(que sólo contiene ficheros cuyos nombres terminan
en .pdb
) da como resultado un mensaje de error que indica
que no existe ningún fichero llamado *.pdf
. Sin embargo,
generalmente comandos como wc
y ls
ven las
listas de nombres de ficheros que coinciden con estas expresiones, pero
no los comodines en sí. Es el shell, no los otros programas, el que
expande los comodines.
Listar los nombres de archivos que coinciden con un patrón
Cuando se ejecuta en el directorio alkanes
, ¿qué
comando(s) ls
producirá(n) este resultado?
ethane.pdb methane.pdb
ls *t*ane.pdb
ls *t?ne.*
ls *t??ne.pdb
ls ethane.*
La solución es 3.
1.
muestra todos los ficheros cuyos nombres contienen
cero o más caracteres (*
) seguidos de la letra
t
, luego cero o más caracteres (*
) seguidos de
ane.pdb
. Esto da
ethane.pdb methane.pdb octane.pdb pentane.pdb
.
2.
muestra todos los ficheros cuyos nombres empiezan por
cero o más caracteres (*
) seguidos de la letra
t
, luego un solo carácter (?
), luego
ne.
seguido de cero o más caracteres (*
). Esto
nos dará octane.pdb
y pentane.pdb
pero no
coincide con nada que termine en thane.pdb
.
3.
soluciona los problemas de la opción 2 haciendo
coincidir dos caracteres (??
) entre t
y
ne
. Esta es la solución.
4.
sólo muestra los archivos que empiezan por
ethane.
.
Más sobre comodines
Sam tiene un directorio que contiene datos de calibración, conjuntos de datos y descripciones de los conjuntos de datos:
BASH
.
├── 2015-10-23-calibration.txt
├── 2015-10-23-dataset1.txt
├── 2015-10-23-dataset2.txt
├── 2015-10-23-dataset_overview.txt
├── 2015-10-26-calibration.txt
├── 2015-10-26-dataset1.txt
├── 2015-10-26-dataset2.txt
├── 2015-10-26-dataset_overview.txt
├── 2015-11-23-calibration.txt
├── 2015-11-23-dataset1.txt
├── 2015-11-23-dataset2.txt
├── 2015-11-23-dataset_overview.txt
├── backup
│ ├── calibration
│ └── datasets
└── send_to_bob
├── all_datasets_created_on_a_23rd
└── all_november_files
Antes de salir a otra excursión, quiere hacer una copia de seguridad de sus datos y enviar algunos conjuntos de datos a su colega Bob. Sam utiliza los siguientes comandos para realizar el trabajo:
BASH
$ cp *dataset* backup/datasets
$ cp ____calibration____ backup/calibration
$ cp 2015-____-____ send_to_bob/all_november_files/
$ cp ____ send_to_bob/all_datasets_created_on_a_23rd/
Ayuda a Sam rellenando los espacios en blanco.
La estructura de directorios resultante debería ser la siguiente
BASH
.
├── 2015-10-23-calibration.txt
├── 2015-10-23-dataset1.txt
├── 2015-10-23-dataset2.txt
├── 2015-10-23-dataset_overview.txt
├── 2015-10-26-calibration.txt
├── 2015-10-26-dataset1.txt
├── 2015-10-26-dataset2.txt
├── 2015-10-26-dataset_overview.txt
├── 2015-11-23-calibration.txt
├── 2015-11-23-dataset1.txt
├── 2015-11-23-dataset2.txt
├── 2015-11-23-dataset_overview.txt
├── backup
│ ├── calibration
│ │ ├── 2015-10-23-calibration.txt
│ │ ├── 2015-10-26-calibration.txt
│ │ └── 2015-11-23-calibration.txt
│ └── datasets
│ ├── 2015-10-23-dataset1.txt
│ ├── 2015-10-23-dataset2.txt
│ ├── 2015-10-23-dataset_overview.txt
│ ├── 2015-10-26-dataset1.txt
│ ├── 2015-10-26-dataset2.txt
│ ├── 2015-10-26-dataset_overview.txt
│ ├── 2015-11-23-dataset1.txt
│ ├── 2015-11-23-dataset2.txt
│ └── 2015-11-23-dataset_overview.txt
└── send_to_bob
├── all_datasets_created_on_a_23rd
│ ├── 2015-10-23-dataset1.txt
│ ├── 2015-10-23-dataset2.txt
│ ├── 2015-10-23-dataset_overview.txt
│ ├── 2015-11-23-dataset1.txt
│ ├── 2015-11-23-dataset2.txt
│ └── 2015-11-23-dataset_overview.txt
└── all_november_files
├── 2015-11-23-calibration.txt
├── 2015-11-23-dataset1.txt
├── 2015-11-23-dataset2.txt
└── 2015-11-23-dataset_overview.txt
Organizar directorios y archivos
Jamie está trabajando en un proyecto, y ve que sus archivos no están muy bien organizados:
SALIDA
analyzed/ fructose.dat raw/ sucrose.dat
Los archivos fructose.dat
y sucrose.dat
contienen los resultados de su análisis de datos. ¿Qué comando(s)
cubierto(s) en esta lección necesita ejecutar para que los comandos de
abajo produzcan la salida mostrada?
SALIDA
analyzed/ raw/
SALIDA
fructose.dat sucrose.dat
Jamie necesita mover sus ficheros fructose.dat
y
sucrose.dat
al directorio analyzed
. El
intérprete de comandos expandirá *.dat para que coincida con todos los
archivos .dat del directorio actual. A continuación, el comando
mv
mueve la lista de archivos .dat al directorio
analizado
.
Reproducir una estructura de carpetas
Está comenzando un nuevo experimento y le gustaría duplicar la estructura de directorios de su experimento anterior para poder añadir nuevos datos.
Supongamos que el experimento anterior está en una carpeta llamada
2016-05-18
, que contiene una carpeta data
que
a su vez contiene carpetas llamadas raw
y
processed
que contienen ficheros de datos. El objetivo es
copiar la estructura de carpetas de la carpeta 2016-05-18
en una carpeta llamada 2016-05-20
de modo que la estructura
final de directorios tenga este aspecto:
SALIDA
2016-05-20/
└── data
├── processed
└── raw
¿Cuál de la siguiente serie de comandos lograría este objetivo? ¿Qué harían los otros comandos?
Los dos primeros conjuntos de comandos logran este objetivo. El primer conjunto utiliza rutas relativas para crear el directorio de nivel superior antes que los subdirectorios.
El tercer conjunto de comandos dará un error porque el comportamiento
por defecto de mkdir
no creará un subdirectorio de un
directorio inexistente: las carpetas de nivel intermedio deben crearse
primero.
El cuarto conjunto de comandos logra este objetivo. Recuerde que la
opción -p
, seguida de una ruta de uno o más directorios,
hará que mkdir
cree los subdirectorios intermedios que sean
necesarios.
El último conjunto de comandos genera los directorios ‘raw’ y ‘processed’ al mismo nivel que el directorio ‘data’.
Puntos Clave
-
cp [old] [new]
copia un archivo. -
mkdir [path]
crea un nuevo directorio. -
mv [old] [new]
mueve (renombra) un archivo o directorio. -
rm [path]
elimina (borra) un archivo. -
*
coincide con cero o más caracteres en un nombre de archivo, por lo que*.txt
coincide con todos los archivos que terminan en.txt
. -
?
coincide con cualquier carácter de un nombre de archivo, por lo que?.txt
coincide cona.txt
pero no conany.txt
. - El uso de la tecla Control puede describirse de muchas maneras,
incluyendo
Ctrl-X
,Control-X
y^X
. - El intérprete de comandos no tiene papelera: una vez que se borra algo, desaparece de verdad.
- Los nombres de la mayoría de los ficheros son
something.extension
. La extensión no es necesaria y no garantiza nada, pero normalmente se utiliza para indicar el tipo de datos del archivo. - Dependiendo del tipo de trabajo que realice, es posible que necesite un editor de texto más potente que Nano.
Content from Pipes y filtros
Última actualización: 2025-07-01 | Mejora esta página
Hoja de ruta
Preguntas
- ¿Cómo puedo combinar comandos existentes para producir una salida deseada?
- ¿Cómo puedo mostrar sólo una parte de la salida?
Objetivos
- Explica la ventaja de enlazar comandos con pipes y filtros.
- Combina secuencias de comandos para obtener una nueva salida
- Redirige la salida de un comando a un archivo.
- Explica qué ocurre normalmente si un programa o una pipe no recibe ninguna entrada para procesar.
Ahora que ya conocemos algunos comandos básicos, por fin podemos
echar un vistazo a la característica más poderosa del shell: la
facilidad con la que nos permite combinar programas existentes de nuevas
formas. Empezaremos con el directorio
shell-lesson-data/exercise-data/alkanes
que contiene seis
archivos que describen algunas moléculas orgánicas simples. La extensión
.pdb
indica que estos archivos están en formato Protein
Data Bank, un formato de texto simple que especifica el tipo y la
posición de cada átomo en la molécula.
SALIDA
cubane.pdb methane.pdb pentane.pdb
ethane.pdb octane.pdb propane.pdb
Vamos a ejecutar un comando de ejemplo:
SALIDA
20 156 1158 cubane.pdb
wc
es el comando ‘word count’: cuenta el número de
líneas, palabras y caracteres en ficheros (devolviendo los valores en
ese orden de izquierda a derecha).
Si ejecutamos el comando wc *.pdb
, el *
en
*.pdb
coincide con cero o más caracteres, por lo que el
shell convierte *.pdb
en una lista de todos los ficheros
.pdb
en el directorio actual:
SALIDA
20 156 1158 cubane.pdb
12 84 622 ethane.pdb
9 57 422 methane.pdb
30 246 1828 octane.pdb
21 165 1226 pentane.pdb
15 111 825 propane.pdb
107 819 6081 total
Observe que wc *.pdb
también muestra el número total de
todas las líneas en la última línea de la salida.
Si ejecutamos wc -l
en lugar de sólo wc
, la
salida muestra sólo el número de líneas por fichero:
SALIDA
20 cubane.pdb
12 ethane.pdb
9 methane.pdb
30 octane.pdb
21 pentane.pdb
15 propane.pdb
107 total
Las opciones -m
y -w
también pueden usarse
con el comando wc
para mostrar sólo el número de caracteres
o el número de palabras, respectivamente.
¿Por qué no hace nada?
¿Qué pasa si un comando debe procesar un fichero, pero no le damos un nombre de fichero? Por ejemplo, ¿qué pasa si escribimos:
pero no escribe *.pdb
(ni nada más) después del comando?
Dado que no tiene ningún nombre de archivo, wc
asume que se
supone que debe procesar la entrada dada en el símbolo del sistema, por
lo que simplemente se sienta allí y espera a que le demos algunos datos
de forma interactiva. Desde el exterior, sin embargo, todo lo que vemos
es que se sienta allí, y el comando no parece hacer nada.
Si comete este tipo de error, puede salir de este estado manteniendo pulsada la tecla control (Ctrl) y pulsando la letra C una vez: Ctrl+C. A continuación, suelta ambas teclas.
Capturando la salida de los comandos
¿Cuál de estos ficheros contiene menos líneas? Es una pregunta fácil de responder cuando sólo hay seis ficheros, pero ¿y si hubiera 6000? Nuestro primer paso hacia la solución es ejecutar el comando
El símbolo mayor que, >
, indica al shell que
redirija la salida del comando a un fichero en lugar de
imprimirla en pantalla. Este comando no imprime ninguna salida en
pantalla, porque todo lo que wc
hubiera impreso ha ido al
fichero lengths.txt
en su lugar. Si el archivo no existe
antes de emitir el comando, el shell creará el archivo. Si el archivo ya
existe, se sobrescribirá silenciosamente, lo que puede provocar la
pérdida de datos. Por lo tanto, los comandos redirigir
requieren precaución.
ls lengths.txt
confirma que el archivo existe:
SALIDA
lengths.txt
Ahora podemos enviar el contenido de lengths.txt
a la
pantalla usando cat lengths.txt
. El comando
cat
recibe su nombre de ‘concatenar’, es decir, unir, e
imprime el contenido de los ficheros uno tras otro. En este caso sólo
hay un fichero, así que cat
sólo nos muestra lo que
contiene:
SALIDA
20 cubane.pdb
12 ethane.pdb
9 methane.pdb
30 octane.pdb
21 pentane.pdb
15 propane.pdb
107 total
Salida Página por Página
Continuaremos usando cat
en esta lección, por
conveniencia y consistencia, pero tiene la desventaja de que siempre
vuelca todo el fichero en la pantalla. Más útil en la práctica es el
comando less
(por ejemplo less lengths.txt
).
Esto muestra un pantallazo del archivo, y luego se detiene. Puedes
avanzar una pantalla pulsando la barra espaciadora, o retroceder una
pulsando b
. Pulsa q
para salir.
Filtrar salida
A continuación usaremos el comando sort
para ordenar el
contenido del archivo lengths.txt
. Pero primero haremos un
ejercicio para aprender un poco sobre el comando sort:
¿Qué hace sort -n
?
El fichero shell-lesson-data/exercise-data/numbers.txt
contiene las siguientes líneas:
10
2
19
22
6
Si ejecutamos sort
en este fichero, la salida es:
SALIDA
10
19
2
22
6
Si ejecutamos sort -n
en el mismo fichero, obtenemos
esto en su lugar:
SALIDA
2
6
10
19
22
Explica por qué -n
tiene este efecto.
La opción -n
especifica una ordenación numérica en lugar
de alfanumérica.
También usaremos la opción -n
para especificar que la
ordenación sea numérica en lugar de alfanumérica. Esto no
cambia el fichero, sino que envía el resultado ordenado a la
pantalla:
SALIDA
9 methane.pdb
12 ethane.pdb
15 propane.pdb
20 cubane.pdb
21 pentane.pdb
30 octane.pdb
107 total
Podemos poner la lista ordenada de líneas en otro fichero temporal
llamado sorted-lengths.txt
poniendo
> sorted-lengths.txt
después del comando, igual que
usamos > lengths.txt
para poner la salida de
wc
en lengths.txt
. Una vez hecho esto, podemos
ejecutar otro comando llamado head
para obtener las
primeras líneas en sorted-lengths.txt
:
SALIDA
9 methane.pdb
Usar -n 1
con head
le dice que sólo
queremos la primera línea del fichero; -n 20
obtendría las
20 primeras, y así sucesivamente. Como sorted-lengths.txt
contiene las longitudes de nuestros ficheros ordenadas de menor a mayor,
la salida de head
debe ser el fichero con menos líneas.
¿Qué significa >>
?
Hemos visto el uso de >
, pero existe un operador
similar >>
que funciona de forma ligeramente
diferente. Aprenderemos las diferencias entre estos dos operadores
imprimiendo algunas cadenas. Podemos utilizar el comando
echo
para imprimir cadenas, por ejemplo
SALIDA
The echo command prints text
Ahora prueba los comandos de abajo para revelar la diferencia entre los dos operadores:
y:
Sugerencia: Intenta ejecutar cada comando dos veces seguidas y luego examina los archivos de salida.
En el primer ejemplo con >
, la cadena ‘hola’ se
escribe en testfile01.txt
, pero el fichero se sobreescribe
cada vez que ejecutamos el comando.
Vemos en el segundo ejemplo que el operador >>
también escribe ‘hola’ en un fichero (en este caso
testfile02.txt
), pero añade la cadena al fichero si ya
existe (es decir, cuando lo ejecutamos por segunda vez).
Añadiendo datos
Ya hemos conocido el comando head
, que imprime líneas
desde el principio de un fichero. el comando tail
es
similar, pero imprime líneas desde el final del fichero.
Considera el fichero
shell-lesson-data/exercise-data/animal-counts/animals.csv
.
Después de estos comandos, seleccione la respuesta que corresponde al
fichero animals-subset.csv
:
- Las tres primeras líneas de
animals.csv
- Las dos últimas líneas de
animals.csv
son - Las tres primeras líneas y las dos últimas líneas de
animals.csv
- La segunda y la tercera línea de
animals.csv
La opción 3 es correcta. Para que la opción 1 sea correcta sólo
ejecutaríamos el comando head
. Para que la opción 2 sea
correcta sólo ejecutaríamos el comando tail
. Para que la
opción 4 sea correcta tendríamos que pasar la salida de
head
a tail -n 2
haciendo
head -n 3 animals.csv | tail -n 2 > animals-subset.csv
Pasando la salida a otro comando
En nuestro ejemplo de encontrar el fichero con menos líneas, estamos
usando dos ficheros intermedios lengths.txt
y
sorted-lengths.txt
para almacenar la salida. Esta es una
forma confusa de trabajar porque incluso una vez que entiendes lo que
hacen wc
, sort
, y head
, esos
ficheros intermedios hacen difícil seguir lo que está pasando. Podemos
hacerlo más fácil de entender ejecutando sort
y
head
juntos:
SALIDA
9 methane.pdb
La barra vertical, |
, entre los dos comandos se llama
pipe. Le dice al shell que queremos usar la salida del
comando de la izquierda como entrada para el comando de la derecha.
Esto ha eliminado la necesidad del archivo
sorted-lengths.txt
.
Combinando múltiples comandos
Nada nos impide encadenar pipes consecutivamente. Podemos, por
ejemplo, enviar la salida de wc
directamente a
sort
, y luego enviar la salida resultante a
head
. Esto elimina la necesidad de ficheros
intermedios.
Empezaremos usando una pipe para enviar la salida de wc
a sort
:
SALIDA
9 methane.pdb
12 ethane.pdb
15 propane.pdb
20 cubane.pdb
21 pentane.pdb
30 octane.pdb
107 total
Podemos entonces enviar esa salida a través de otra pipe, a
head
, de modo que la pipe completa se convierte en:
SALIDA
9 methane.pdb
Esto es exactamente como si un matemático anidara funciones como
log(3x) y dijera ‘el logaritmo de tres veces x’. En
nuestro caso, el algoritmo es ‘cabeza de ordenación del recuento de
líneas de *.pdb
’.
La redirección y las pipes utilizadas en los últimos comandos se ilustran a continuación:
Uniendo comandos en pipes
En nuestro directorio actual, queremos encontrar los 3 archivos que tienen el menor número de líneas. ¿Qué comando de los que aparecen a continuación funcionaría?
wc -l * > sort -n > head -n 3
wc -l * | sort -n | head -n 1-3
wc -l * | head -n 3 | sort -n
wc -l * | sort -n | head -n 3
La opción 4 es la solución. El carácter |
se utiliza
para conectar la salida de un comando a la entrada de otro. el carácter
>
se utiliza para redirigir la salida estándar a un
archivo. ¡Pruébalo en el directorio
shell-lesson-data/exercise-data/alkanes
!
Herramientas diseñadas para trabajar juntas
Esta idea de enlazar programas es la razón por la que Unix ha tenido
tanto éxito. En lugar de crear enormes programas que intentan hacer
muchas cosas diferentes, los programadores de Unix se centran en crear
un montón de herramientas simples que cada una hace bien un trabajo, y
que funcionan bien entre sí. Este modelo de programación se denomina
“pipes y filtros”. Ya hemos visto las pipes; un filtro
es un programa como wc
o sort
que transforma
un flujo de entrada en un flujo de salida. Casi todas las herramientas
estándar de Unix pueden trabajar de esta manera. A menos que se les
indique lo contrario, leen de la entrada estándar, hacen algo con lo que
han leído y escriben en la salida estándar.
La clave es que cualquier programa que lea líneas de texto de la entrada estándar y escriba líneas de texto en la salida estándar puede combinarse con cualquier otro programa que también se comporte de esta manera. Puedes y debes escribir tus programas de esta forma para que tú y otras personas podáis poner esos programas en pipes para multiplicar su potencia.
Comprensión de Lectura de pipes
Un fichero llamado animals.csv
(en la carpeta
shell-lesson-data/exercise-data/animal-counts
) contiene los
siguientes datos:
2012-11-05,deer,5
2012-11-05,rabbit,22
2012-11-05,raccoon,7
2012-11-06,rabbit,19
2012-11-06,deer,2
2012-11-06,fox,4
2012-11-07,rabbit,16
2012-11-07,bear,1
¿Qué texto pasa a través de cada una de las pipes y la redirección
final en la pipe de abajo? Nota, el comando sort -r
ordena
en orden inverso.
Pista: construye la pipe comando a comando para comprobar tu comprensión
El comando head
extrae las 5 primeras líneas de
animals.csv
. A continuación, se extraen las 3 últimas
líneas de las 5 anteriores mediante el comando tail
. Con el
comando sort -r
esas 3 líneas se ordenan en orden inverso.
Finalmente, la salida se redirige a un fichero: final.txt
.
El contenido de este fichero puede comprobarse ejecutando
cat final.txt
. El fichero debería contener las siguientes
líneas
2012-11-06,rabbit,19
2012-11-06,deer,2
2012-11-05,raccoon,7
Construcción de pipes
Para el fichero animals.csv
del ejercicio anterior,
considera el siguiente comando:
El comando cut
se utiliza para eliminar o ‘cortar’
ciertas secciones de cada línea del fichero, y cut
espera
que las líneas estén separadas en columnas por un carácter
Tab. Un carácter utilizado de esta forma se denomina
delimitador. En el ejemplo anterior utilizamos la
opción -d
para especificar la coma como carácter
delimitador. También hemos utilizado la opción -f
para
especificar que queremos extraer el segundo campo (columna). El
resultado es el siguiente
SALIDA
deer
rabbit
raccoon
rabbit
deer
fox
rabbit
bear
El comando uniq
filtra las líneas adyacentes
coincidentes en un fichero. ¿Cómo podrías extender este conducto (usando
uniq
y otro comando) para averiguar qué animales contiene
el fichero (sin duplicados en sus nombres)?
¿Qué pipe?
El fichero animals.csv
contiene 8 líneas de datos
formateadas de la siguiente manera:
SALIDA
2012-11-05,deer,5
2012-11-05,rabbit,22
2012-11-05,raccoon,7
2012-11-06,rabbit,19
...
El comando uniq
tiene una opción -c
que
proporciona un recuento del número de veces que una línea aparece en su
entrada. Suponiendo que tu directorio actual es
shell-lesson-data/exercise-data/animal-counts
, ¿qué comando
usarías para producir una tabla que muestre el recuento total de cada
tipo de animal en el fichero?
sort animals.csv | uniq -c
sort -t, -k2,2 animals.csv | uniq -c
cut -d, -f 2 animals.csv | uniq -c
cut -d, -f 2 animals.csv | sort | uniq -c
cut -d, -f 2 animals.csv | sort | uniq -c | wc -l
La opción 4. es la respuesta correcta. Si tienes dificultades para
entender por qué, intenta ejecutar los comandos, o subsecciones de las
pipes (asegúrate de que estás en el directorio
shell-lesson-data/exercise-data/animal-counts
).
Pipe de Nelle: Comprobación de archivos
Nelle ha pasado sus muestras por las máquinas de ensayo y ha creado
17 archivos en el directorio north-pacific-gyre
descrito
anteriormente. Como comprobación rápida, partiendo del directorio
shell-lesson-data
, Nelle teclea:
La salida son 18 líneas que tienen este aspecto:
SALIDA
300 NENE01729A.txt
300 NENE01729B.txt
300 NENE01736A.txt
300 NENE01751A.txt
300 NENE01751B.txt
300 NENE01812A.txt
... ...
Ahora escribe esto:
SALIDA
240 NENE02018B.txt
300 NENE01729A.txt
300 NENE01729B.txt
300 NENE01736A.txt
300 NENE01751A.txt
Ups: uno de los ficheros es 60 líneas más corto que los otros. Cuando vuelve y lo comprueba, ve que hizo ese ensayo a las 8:00 de un lunes por la mañana — probablemente alguien estuvo usando la máquina el fin de semana, y se olvidó de reiniciarla. Antes de volver a ejecutar esa muestra, comprueba si algún archivo tiene demasiados datos:
SALIDA
300 NENE02040B.txt
300 NENE02040Z.txt
300 NENE02043A.txt
300 NENE02043B.txt
5040 total
Esos números tienen buena pinta — pero ¿qué hace esa ‘Z’ ahí en la antepenúltima línea? Todas sus muestras deberían estar marcadas con ‘A’ o ‘B’; por convención, su laboratorio utiliza la ‘Z’ para indicar las muestras a las que les falta información. Para encontrar otras iguales, hace esto:
SALIDA
NENE01971Z.txt NENE02040Z.txt
Efectivamente, cuando comprueba el registro en su portátil, no hay
ninguna profundidad registrada para ninguna de esas muestras. Como es
demasiado tarde para obtener la información de otra forma, debe excluir
esos dos archivos de su análisis. Podría eliminarlos utilizando
rm
, pero hay algunos análisis que podría hacer más adelante
en los que la profundidad no importa, así que en su lugar, tendrá que
tener cuidado más adelante para seleccionar los archivos utilizando las
expresiones comodín NENE*A.txt NENE*B.txt
.
Eliminar archivos innecesarios
Supongamos que desea eliminar los archivos de datos procesados y
conservar sólo los archivos sin procesar y el script de procesamiento
para ahorrar espacio. Los archivos sin procesar terminan en
.dat
y los archivos procesados terminan en
.txt
. ¿Cuál de las siguientes opciones eliminaría todos los
ficheros de datos procesados, y sólo los ficheros de datos
procesados?
rm ?.txt
rm *.txt
rm * .txt
rm *.*
- Esto eliminaría los archivos
.txt
con nombres de un carácter - Esta es la respuesta correcta
- El shell expandiría
*
para coincidir con todo lo que hay en el directorio actual, por lo que el comando intentaría eliminar todos los ficheros coincidentes y un fichero adicional llamado.txt
- El shell expande
*.*
para que coincida con todos los nombres de fichero que contengan al menos un.
, incluyendo los ficheros procesados (.txt
) y los ficheros sin procesar (.dat
)
Puntos Clave
-
wc
cuenta líneas, palabras y caracteres en sus entradas. -
cat
muestra el contenido de sus entradas. -
sort
ordena sus entradas. -
head
muestra por defecto las 10 primeras líneas de su entrada sin argumentos adicionales. -
tail
muestra por defecto las 10 últimas líneas de su entrada sin argumentos adicionales. -
command > [file]
redirige la salida de un comando a un archivo (sobreescribiendo cualquier contenido existente). -
command >> [file]
añade la salida de un comando a un archivo. -
[first] | [second]
es una pipe: la salida del primer comando se usa como entrada del segundo. - La mejor manera de usar el shell es usar pipes para combinar programas simples de un solo propósito (filtros).
Content from Bucles
Última actualización: 2025-04-03 | Mejora esta página
Hoja de ruta
Preguntas
- ¿Cómo puedo realizar las mismas acciones en muchos archivos diferentes?
Objetivos
- Escribe un bucle que aplique uno o más comandos por separado a cada fichero de un conjunto de ficheros.
- Trazar los valores que toma una variable del bucle durante la ejecución del mismo.
- Explica la diferencia entre el nombre de una variable y su valor.
- Explique por qué los espacios y algunos caracteres de puntuación no deben usarse en los nombres de archivo.
- Demuestra cómo ver qué comandos se han ejecutado recientemente.
- Reejecuta los comandos ejecutados recientemente sin volver a escribirlos.
Los bucles son una construcción de programación que nos permite repetir un comando o conjunto de comandos para cada elemento de una lista. Como tales, son clave para mejorar la productividad mediante la automatización. Al igual que los comodines y el tabulador, el uso de bucles también reduce la cantidad de texto necesario (y, por tanto, el número de errores tipográficos).
Supongamos que tenemos varios cientos de archivos de datos genómicos
llamados basilisk.dat
, minotaur.dat
y
unicorn.dat
. Para este ejemplo, utilizaremos el directorio
exercise-data/creatures
que sólo tiene tres ficheros de
ejemplo, pero los principios pueden aplicarse a muchos más ficheros a la
vez.
La estructura de estos ficheros es la misma: el nombre común, la clasificación y la fecha de actualización se presentan en las tres primeras líneas, con las secuencias de ADN en las líneas siguientes. Veamos los ficheros
Queremos imprimir la clasificación de cada especie, que aparece en la
segunda línea de cada fichero. Para cada archivo, tendríamos que
ejecutar el comando head -n 2
y canalizarlo a
tail -n 1
. Utilizaremos un bucle para resolver este
problema, pero primero veamos la forma general de un bucle, utilizando
el pseudocódigo siguiente:
BASH
# The word "for" indicates the start of a "For-loop" command
for thing in list_of_things
#The word "do" indicates the start of job execution list
do
# Indentation within the loop is not required, but aids legibility
operation_using/command $thing
# The word "done" indicates the end of a loop
done
y podemos aplicarlo a nuestro ejemplo de la siguiente manera:
BASH
$ for filename in basilisk.dat minotaur.dat unicorn.dat
> do
> echo $filename
> head -n 2 $filename | tail -n 1
> done
SALIDA
basilisk.dat
CLASSIFICATION: basiliscus vulgaris
minotaur.dat
CLASSIFICATION: bos hominus
unicorn.dat
CLASSIFICATION: equus monoceros
Siga las instrucciones
El prompt del shell cambia de $
a >
y
viceversa mientras escribíamos en nuestro bucle. El segundo prompt,
>
, es diferente para recordarnos que aún no hemos
terminado de escribir un comando completo. Se puede utilizar un punto y
coma, ;
, para separar dos órdenes escritas en una sola
línea.
Cuando el shell ve la palabra clave for
, sabe que debe
repetir un comando (o grupo de comandos) una vez por cada elemento de
una lista. Cada vez que se ejecuta el bucle (lo que se denomina una
iteración), se asigna un elemento de la lista en secuencia a la
variable, y se ejecutan los comandos dentro del bucle,
antes de pasar al siguiente elemento de la lista. Dentro del bucle,
pedimos el valor de la variable poniendo $
delante de ella.
El $
le dice al intérprete del shell que trate la variable
como un nombre de variable y sustituya su valor en su lugar, en lugar de
tratarlo como texto o un comando externo.
En este ejemplo, la lista es de tres nombres de fichero:
basilisk.dat
, minotaur.dat
, y
unicorn.dat
. Cada vez que el bucle itera, primero usamos
echo
para imprimir el valor que tiene actualmente la
variable $filename
. Esto no es necesario para el resultado,
pero nos beneficia aquí para seguirlo más fácilmente. A continuación,
ejecutaremos el comando head
en el fichero referenciado
actualmente por $filename
. La primera vez a través del
bucle, $filename
es basilisk.dat
. El
intérprete ejecuta el comando head
en
basilisk.dat
y envía las dos primeras líneas al comando
tail
, que a su vez imprime la segunda línea de
basilisk.dat
. Para la segunda iteración,
$filename
se convierte en minotaur.dat
. Esta
vez, el shell ejecuta head
en minotaur.dat
y
envía las dos primeras líneas al comando tail
, que imprime
la segunda línea de minotaur.dat
. Para la tercera
iteración, $filename
se convierte en
unicorn.dat
, así que el shell ejecuta el comando
head
en ese fichero, y tail
en la salida del
mismo. Como la lista sólo tenía tres elementos, la shell sale del bucle
for
.
Mismos símbolos, diferentes significados
Aquí vemos que >
se utiliza como prompt del shell,
mientras que >
también se utiliza para redirigir la
salida. De forma similar, $
se usa como prompt del shell,
pero, como vimos antes, también se usa para pedir al shell que obtenga
el valor de una variable.
Si el shell imprime >
o $
entonces espera que escribas algo, y el símbolo es un prompt.
Si usted mismo teclea >
o $
, es
una instrucción suya para que el shell redirija la salida u obtenga el
valor de una variable.
Cuando se usan variables también es posible poner los nombres entre
llaves para delimitar claramente el nombre de la variable:
$filename
es equivalente a ${filename}
, pero
es diferente de ${file}name
. Puedes encontrar esta notación
en los programas de otras personas.
Hemos llamado a la variable en este bucle filename
para
que su propósito sea más claro para los lectores humanos. Al shell en sí
no le importa cómo se llama la variable; si escribiéramos este bucle
como:
o:
BASH
$ for temperature in basilisk.dat minotaur.dat unicorn.dat
> do
> head -n 2 $temperature | tail -n 1
> done
funcionaría exactamente igual. No haga esto. Los programas
sólo son útiles si la gente puede entenderlos, así que los nombres sin
sentido (como x
) o los nombres engañosos (como
temperature
) aumentan las probabilidades de que el programa
no haga lo que sus lectores piensan que hace.
En los ejemplos anteriores, las variables (thing
,
filename
, x
y temperature
)
podrían haber recibido cualquier otro nombre, siempre que sea
significativo tanto para la persona que escribe el código como para la
que lo lee.
Observe también que los bucles pueden usarse para otras cosas que no sean nombres de ficheros, como una lista de números o un subconjunto de datos.
Escribe tu propio bucle
¿Cómo escribirías un bucle que se haga eco de los 10 números del 0 al 9?
Variables en bucles
Este ejercicio se refiere al directorio
shell-lesson-data/exercise-data/alkanes
.ls *.pdb
da la siguiente salida:
SALIDA
cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb
¿Cuál es la salida del siguiente código?
Ahora, ¿cuál es la salida del siguiente código?
¿Por qué estos dos bucles dan salidas diferentes?
El primer bloque de código da la misma salida en cada iteración del
bucle. Bash expande el comodín *.pdb
dentro del cuerpo del
bucle (así como antes de que comience el bucle) para que coincida con
todos los archivos que terminan en .pdb
y luego los enumera
utilizando ls
. El bucle expandido tendría este aspecto
BASH
$ for datafile in cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb
> do
> ls cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb
> done
SALIDA
cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb
cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb
cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb
cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb
cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb
cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb
El segundo bloque de código lista un fichero diferente en cada
iteración del bucle. El valor de la variable datafile
se
evalúa con $datafile
, y luego se lista con
ls
.
SALIDA
cubane.pdb
ethane.pdb
methane.pdb
octane.pdb
pentane.pdb
propane.pdb
Limitación de conjuntos de ficheros
4 es la respuesta correcta.*
coincide con cero o más
caracteres, por lo que cualquier nombre de archivo que empiece por la
letra c, seguida de cero o más caracteres será coincidente.
Limitación de conjuntos de ficheros (continued)
¿En qué se diferenciaría la salida de este comando?
- Se listarían los mismos ficheros.
- Esta vez se listan todos los ficheros.
- No hay ficheros listados esta vez.
- Se listarán los ficheros
cubane.pdb
yoctane.pdb
. - Sólo se listará el fichero
octane.pdb
.
4 es la respuesta correcta. *
coincide con cero o más
caracteres, por lo que un nombre de archivo con cero o más caracteres
antes de la letra c y cero o más caracteres después de la letra c será
coincidente.
Guardar en un archivo en un bucle - Primera parte
En el directorio
shell-lesson-data/exercise-data/alkanes
, ¿cuál es el efecto
de este bucle?
- Imprime
cubane.pdb
,ethane.pdb
,methane.pdb
,octane.pdb
,pentane.pdb
ypropane.pdb
, y el texto depropane.pdb
se guardará en un fichero llamadoalkanes.pdb
. - Imprime
cubane.pdb
,ethane.pdb
, ymethane.pdb
, y el texto de los tres ficheros se concatena y se guarda en un fichero llamadoalkanes.pdb
. - Imprime
cubane.pdb
,ethane.pdb
,methane.pdb
,octane.pdb
, ypentane.pdb
, y el texto depropane.pdb
se guardará en un fichero llamadoalkanes.pdb
. - Ninguna de las anteriores.
- El texto de cada fichero se escribe en el fichero
alkanes.pdb
. Sin embargo, el fichero se sobreescribe en cada iteración del bucle, por lo que el contenido final dealkanes.pdb
es el texto del ficheropropane.pdb
.
Guardar en un archivo en un bucle - Segunda parte
También en el directorio
shell-lesson-data/exercise-data/alkanes
, ¿cuál sería la
salida del siguiente bucle?
- Todo el texto de
cubane.pdb
,ethane.pdb
,methane.pdb
,octane.pdb
, ypentane.pdb
sería concatenado y guardado en un fichero llamadoall.pdb
. - El texto de
ethane.pdb
se guardará en un fichero llamadoall.pdb
. - Todo el texto de
cubane.pdb
,ethane.pdb
,methane.pdb
,octane.pdb
,pentane.pdb
ypropane.pdb
sería concatenado y guardado en un fichero llamadoall.pdb
. - Todo el texto de
cubane.pdb
,ethane.pdb
,methane.pdb
,octane.pdb
,pentane.pdb
ypropane.pdb
se imprimiría en la pantalla y se guardaría en un fichero llamadoall.pdb
.
3 es la respuesta correcta. >>
añade a un archivo,
en lugar de sobrescribirlo con la salida redirigida de un comando. Dado
que la salida del comando cat
ha sido redirigida, no se
imprime nada en la pantalla.
Continuemos con nuestro ejemplo en el directorio
shell-lesson-data/exercise-data/creatures
. Aquí tenemos un
bucle un poco más complicado:
El shell comienza expandiendo *.dat
para crear la lista
de ficheros que procesará. A continuación, el cuerpo de
bucle ejecuta dos comandos para cada uno de esos ficheros. El
primer comando, echo
, imprime sus argumentos de línea de
comandos en la salida estándar. Por ejemplo
imprime:
SALIDA
hello there
En este caso, como el shell expande $filename
para que
sea el nombre de un fichero, echo $filename
imprime el
nombre del fichero. Tenga en cuenta que no podemos escribir esto
como:
porque entonces la primera vez a través del bucle, cuando
$filename
se expandió a basilisk.dat
, el shell
intentaría ejecutar basilisk.dat
como un programa.
Finalmente, la combinación head
y tail
selecciona las líneas 81-100 de cualquier fichero que esté siendo
procesado (asumiendo que el fichero tiene al menos 100 líneas).
Espacios en los nombres
Los espacios se utilizan para separar los elementos de la lista que vamos a recorrer. Si uno de esos elementos contiene un espacio, debemos rodearlo de comillas y hacer lo mismo con nuestra variable de bucle. Supongamos que nuestros ficheros de datos se llaman
red dragon.dat
purple unicorn.dat
Para hacer un bucle sobre estos ficheros, tendríamos que añadir comillas dobles de la siguiente manera:
BASH
$ for filename in "red dragon.dat" "purple unicorn.dat"
> do
> head -n 100 "$filename" | tail -n 20
> done
Es más sencillo evitar el uso de espacios (u otros caracteres especiales) en los nombres de archivo.
Los ficheros anteriores no existen, por lo que si ejecutamos el
código anterior, el comando head
será incapaz de
encontrarlos; sin embargo, el mensaje de error devuelto mostrará el
nombre de los ficheros que está esperando:
ERROR
head: cannot open ‘red dragon.dat' for reading: No such file or directory
head: cannot open ‘purple unicorn.dat' for reading: No such file or directory
Intente eliminar las comillas alrededor de $filename
en
el bucle anterior para ver el efecto de las comillas sobre los espacios.
Observe que obtenemos un resultado del comando de bucle para unicorn.dat
cuando ejecutamos este código en el directorio
creatures
:
SALIDA
head: cannot open ‘red' for reading: No such file or directory
head: cannot open ‘dragon.dat' for reading: No such file or directory
head: cannot open ‘purple' for reading: No such file or directory
CGGTACCGAA
AAGGGTCGCG
CAAGTGTTCC
...
Queremos modificar cada uno de los ficheros de
shell-lesson-data/exercise-data/creatures
, pero también
guardar una versión de los ficheros originales. Queremos copiar los
ficheros originales en nuevos ficheros llamados
original-basilisk.dat
y original-unicorn.dat
,
por ejemplo. No podemos utilizar
porque se expandiría a:
Esto no respaldaría nuestros archivos, en su lugar obtendríamos un error:
ERROR
cp: target `original-*.dat' is not a directory
Este problema surge cuando cp
recibe más de dos
entradas. Cuando esto ocurre, espera que la última entrada sea un
directorio en el que pueda copiar todos los archivos que se le han
pasado. Como no hay ningún directorio llamado
original-*.dat
en el directorio creatures
,
obtenemos un error.
En su lugar, podemos utilizar un bucle:
Este bucle ejecuta el comando cp
una vez por cada nombre
de fichero. La primera vez, cuando $filename
se expande a
basilisk.dat
, el shell ejecuta:
La segunda vez, el comando es:
La tercera y última vez, el comando es:
Como el comando cp
normalmente no produce ninguna
salida, es difícil comprobar que el bucle funciona correctamente. Sin
embargo, aprendimos antes cómo imprimir cadenas usando
echo
, y podemos modificar el bucle para usar
echo
para imprimir nuestros comandos sin ejecutarlos
realmente. Así podemos comprobar qué comandos se ejecutarían en
el bucle sin modificar.
El siguiente diagrama muestra lo que ocurre cuando se ejecuta el
bucle modificado y demuestra cómo el uso juicioso de echo
es una buena técnica de depuración.
Nelle’s Pipeline: Procesamiento de archivos
Nelle está ahora lista para procesar sus ficheros de datos usando
goostats.sh
— un script escrito por su supervisor. Esto
calcula algunas estadísticas de un archivo de muestra de proteína y toma
dos argumentos:
- un archivo de entrada (que contiene los datos en bruto)
- un archivo de salida (para almacenar las estadísticas calculadas)
Como todavía está aprendiendo a usar el shell, decide construir los
comandos necesarios por etapas. Su primer paso es asegurarse de que
puede seleccionar los archivos de entrada correctos — recuerde, son
aquellos cuyos nombres terminan en ‘A’ o ‘B’, en lugar de ‘Z’. En el
directorio north-pacific-gyre
, Nelle escribe:
BASH
$ cd
$ cd Desktop/shell-lesson-data/north-pacific-gyre
$ for datafile in NENE*A.txt NENE*B.txt
> do
> echo $datafile
> done
SALIDA
NENE01729A.txt
NENE01736A.txt
NENE01751A.txt
...
NENE02040B.txt
NENE02043B.txt
Su siguiente paso es decidir cómo llamar a los archivos que creará el
programa de análisis goostats.sh
. Prefijar el nombre de
cada archivo de entrada con ‘stats’ parece sencillo, así que modifica su
bucle para hacerlo:
SALIDA
NENE01729A.txt stats-NENE01729A.txt
NENE01736A.txt stats-NENE01729A.txt
NENE01751A.txt stats-NENE01729A.txt
...
NENE02040B.txt stats-NENE02040B.txt
NENE02043B.txt stats-NENE02043B.txt
Todavía no ha ejecutado goostats.sh
, pero ahora está
segura de que puede seleccionar los archivos correctos y generar los
nombres de archivo de salida correctos.
Sin embargo, escribir los comandos una y otra vez se está volviendo tedioso, y a Nelle le preocupa cometer errores, así que en lugar de volver a entrar en su bucle, pulsa ↑. En respuesta, el intérprete de comandos vuelve a mostrar todo el bucle en una línea (utilizando punto y coma para separar las piezas):
Usando el ←, Nelle navega hasta el comando
echo
y lo cambia por bash goostats.sh
:
Cuando pulsa Enter, el intérprete de comandos ejecuta el comando modificado. Sin embargo, no parece ocurrir nada: no hay salida. Después de un momento, Nelle se da cuenta de que como su script ya no imprime nada en la pantalla, no tiene ni idea de si se está ejecutando, y mucho menos de lo rápido que lo está haciendo. Ella mata el comando en ejecución escribiendo Ctrl+C, utiliza ↑ para repetir el comando, y lo edita para que se lea:
BASH
$ for datafile in NENE*A.txt NENE*B.txt; do echo $datafile;
bash goostats.sh $datafile stats-$datafile; done
Principio y Fin
Podemos movernos al principio de una línea en el shell tecleando Ctrl+A y al final usando Ctrl+E.
Cuando ejecuta su programa ahora, produce una línea de salida cada cinco segundos más o menos:
SALIDA
NENE01729A.txt
NENE01736A.txt
NENE01751A.txt
...
1518 veces 5 segundos, dividido por 60, le dice que su script tardará
unas dos horas en ejecutarse. Como última comprobación, abre otra
ventana de terminal, entra en north-pacific-gyre
y utiliza
cat stats-NENE01729B.txt
para examinar uno de los archivos
de salida. Tiene buena pinta, así que decide tomarse un café y ponerse
al día con la lectura.
Los que conocen la historia pueden elegir repetirla
Otra forma de repetir el trabajo anterior es utilizar el comando
history
para obtener una lista de los últimos cientos de
comandos que se han ejecutado, y luego utilizar !123
(donde
123
se sustituye por el número de comando) para repetir uno
de esos comandos. Por ejemplo, si Nelle escribe esto
SALIDA
456 for datafile in NENE*A.txt NENE*B.txt; do echo $datafile stats-$datafile; done
457 for datafile in NENE*A.txt NENE*B.txt; do echo $datafile stats-$datafile; done
458 for datafile in NENE*A.txt NENE*B.txt; do bash goostats.sh $datafile stats-$datafile; done
459 for datafile in NENE*A.txt NENE*B.txt; do echo $datafile; bash goostats.sh $datafile
stats-$datafile; done
460 history | tail -n 5
entonces puede volver a ejecutar goostats.sh
en los
archivos simplemente escribiendo !459
.
Otros comandos del historial
Existen otros comandos de acceso directo para acceder al historial.
- Ctrl+R entra en un modo de búsqueda en el historial ‘reverse-i-search’ y encuentra el comando más reciente de tu historial que coincida con el texto que introduzcas a continuación. Pulsa Ctrl+R una o varias veces más para buscar coincidencias anteriores. A continuación, puede utilizar las teclas de flecha izquierda y derecha para elegir esa línea y editarla y, a continuación, pulsar Return para ejecutar el comando.
-
!!
recupera el comando inmediatamente anterior (puede o no encontrar esto más conveniente que usar ↑) -
!$
recupera la última palabra de la última orden. Esto es útil más a menudo de lo que esperas: después debash goostats.sh NENE01729B.txt stats-NENE01729B.txt
, puedes teclearless !$
para ver el ficherostats-NENE01729B.txt
, lo que es más rápido que hacer ↑ y editar la línea de órdenes.
Ejecución en seco
Un bucle es una forma de hacer muchas cosas a la vez — o de cometer
muchos errores a la vez si hace lo incorrecto. Una forma de comprobar lo
que un bucle haría es echo
los comandos que
ejecutaría en lugar de ejecutarlos realmente.
Supongamos que queremos previsualizar los comandos que ejecutará el siguiente bucle sin ejecutar realmente esos comandos:
¿Cuál es la diferencia entre los dos bucles de abajo, y cuál de ellos queremos ejecutar?
La segunda versión es la que queremos ejecutar. Esto imprime en
pantalla todo lo que está entre comillas, expandiendo el nombre de la
variable de bucle porque lo hemos prefijado con un signo de dólar.
Tampoco modifica ni crea el fichero all.pdb
, ya
que el >>
se trata literalmente como parte de una
cadena y no como una instrucción de redirección.
La primera versión añade la salida del comando
echo cat $datafile
al fichero, all.pdb
. Este
fichero sólo contendrá la lista: cat cubane.pdb
,
cat ethane.pdb
, cat methane.pdb
etc.
¡Pruebe usted mismo ambas versiones para ver el resultado! Asegúrese
de abrir el archivo all.pdb
para ver su contenido.
Bucles anidados
Supongamos que queremos establecer una estructura de directorios para organizar algunos experimentos que miden constantes de velocidad de reacción con diferentes compuestos y diferentes temperaturas. ¿Cuál sería el resultado del siguiente código:
Tenemos un bucle anidado, es decir, contenido dentro de otro bucle, por lo que para cada especie en el bucle exterior, el bucle interior (el bucle anidado) itera sobre la lista de temperaturas, y crea un nuevo directorio para cada combinación.
¡Intente ejecutar el código usted mismo para ver qué directorios se crean!
Puntos Clave
- Un bucle
for
repite los comandos una vez por cada cosa de una lista. - Cada bucle
for
necesita una variable para referirse a lo que está operando en ese momento. - Utilice
$name
para expandir una variable (es decir, obtener su valor). también se puede utilizar${name}
. - No utilice espacios, comillas o caracteres comodín como ‘*’ o ‘?’ en los nombres de fichero, ya que complica la expansión de variables.
- Dar a los ficheros nombres consistentes que sean fáciles de emparejar con patrones comodín para facilitar su selección para el bucle.
- Utilice la tecla de flecha arriba para desplazarse por los comandos anteriores para editarlos y repetirlos.
- Utilice Ctrl+R para buscar entre los comandos introducidos anteriormente.
- Usa
history
para mostrar comandos recientes, y![number]
para repetir un comando por número.
Content from Scripts de shell
Última actualización: 2025-04-03 | Mejora esta página
Hoja de ruta
Preguntas
- ¿Cómo puedo guardar y reutilizar comandos?
Objetivos
- Escribe un script de shell que ejecute un comando o una serie de comandos para un conjunto fijo de archivos.
- Ejecuta un script de shell desde la línea de comandos.
- Escribe un script de shell que opera sobre un conjunto de ficheros definidos por el usuario en la línea de comandos.
- Crear pipelines que incluyan shell scripts que tú, y otros, hayáis escrito.
Por fin estamos listos para ver por qué el shell es un entorno de programación tan potente. Vamos a tomar los comandos que repetimos con frecuencia y guardarlos en archivos para que podamos volver a ejecutar todas esas operaciones más tarde escribiendo un solo comando. Por razones históricas, un montón de comandos guardados en un archivo se suele llamar script de shell, pero no te equivoques: en realidad son pequeños programas.
Escribir secuencias de comandos no sólo agilizará el trabajo, sino que también evitará tener que volver a escribir los mismos comandos una y otra vez. También lo hará más preciso (menos posibilidades de errores tipográficos) y más reproducible. Si vuelves a tu trabajo más tarde (o si alguien más encuentra tu trabajo y quiere basarse en él), podrás reproducir los mismos resultados simplemente ejecutando tu script, en lugar de tener que recordar o volver a escribir una larga lista de comandos.
Empecemos volviendo a alkanes/
y creando un nuevo
fichero, middle.sh
que se convertirá en nuestro shell
script:
El comando nano middle.sh
abre el archivo
middle.sh
dentro del editor de texto ‘nano’ (que se ejecuta
dentro del shell). Si el fichero no existe, se creará. Podemos utilizar
el editor de texto para editar directamente el archivo insertando la
siguiente línea:
head -n 15 octane.pdb | tail -n 5
Esta es una variación de la tubería que construimos antes, que
selecciona las líneas 11-15 del archivo octane.pdb
.
Recuerda, no estamos ejecutándolo como un comando todavía; sólo
estamos incorporando los comandos en un archivo.
Luego guardamos el archivo (Ctrl-O
en nano) y salimos
del editor de texto (Ctrl-X
en nano). Comprueba que el
directorio alkanes
contiene ahora un fichero llamado
middle.sh
.
Una vez guardado el fichero, podemos pedir al intérprete de órdenes
que ejecute los comandos que contiene. Nuestro shell se llama
bash
, así que ejecutamos el siguiente comando:
SALIDA
ATOM 9 H 1 -4.502 0.681 0.785 1.00 0.00
ATOM 10 H 1 -5.254 -0.243 -0.537 1.00 0.00
ATOM 11 H 1 -4.357 1.252 -0.895 1.00 0.00
ATOM 12 H 1 -3.009 -0.741 -1.467 1.00 0.00
ATOM 13 H 1 -3.172 -1.337 0.206 1.00 0.00
Efectivamente, la salida de nuestro script es exactamente la que obtendríamos si ejecutáramos ese pipeline directamente.
Texto vs. Lo que sea
Solemos llamar “editores de texto” a programas como Microsoft Word o
LibreOffice Writer, pero tenemos que ser un poco más cuidadosos cuando
se trata de programar. Por defecto, Microsoft Word utiliza archivos
.docx
para almacenar no sólo texto, sino también
información de formato sobre fuentes, encabezados, etcétera. Esta
información adicional no se almacena como caracteres y no significa nada
para herramientas como head
, que espera que los archivos de
entrada no contengan más que las letras, dígitos y signos de puntuación
de un teclado de ordenador estándar. Por lo tanto, al editar programas,
debe utilizar un editor de texto sin formato o tener cuidado de guardar
los archivos como texto sin formato.
¿Y si queremos seleccionar líneas de un archivo arbitrario? Podríamos
editar middle.sh
cada vez para cambiar el nombre del
archivo, pero eso probablemente nos llevaría más tiempo que volver a
escribir el comando en el intérprete de comandos y ejecutarlo con un
nuevo nombre de archivo. En su lugar, vamos a editar
middle.sh
y hacerlo más versátil:
Ahora, dentro de “nano”, sustituye el texto octane.pdb
por la variable especial llamada $1
:
head -n 15 "$1" | tail -n 5
Dentro de un script de shell, $1
significa ‘el primer
nombre de archivo (u otro argumento) en la línea de comandos’. Ahora
podemos ejecutar nuestro script así
SALIDA
ATOM 9 H 1 -4.502 0.681 0.785 1.00 0.00
ATOM 10 H 1 -5.254 -0.243 -0.537 1.00 0.00
ATOM 11 H 1 -4.357 1.252 -0.895 1.00 0.00
ATOM 12 H 1 -3.009 -0.741 -1.467 1.00 0.00
ATOM 13 H 1 -3.172 -1.337 0.206 1.00 0.00
o en un archivo diferente como este:
SALIDA
ATOM 9 H 1 1.324 0.350 -1.332 1.00 0.00
ATOM 10 H 1 1.271 1.378 0.122 1.00 0.00
ATOM 11 H 1 -0.074 -0.384 1.288 1.00 0.00
ATOM 12 H 1 -0.048 -1.362 -0.205 1.00 0.00
ATOM 13 H 1 -1.183 0.500 -1.412 1.00 0.00
Comillas dobles alrededor de los argumentos
Por la misma razón que ponemos la variable de bucle dentro de
comillas dobles, en caso de que el nombre del fichero contenga espacios,
rodeamos $1
con comillas dobles.
Actualmente, tenemos que editar middle.sh
cada vez que
queremos ajustar el rango de líneas que se devuelve. Vamos a
solucionarlo configurando nuestro script para que utilice tres
argumentos de línea de comandos. Después del primer argumento de línea
de órdenes ($1
), cada argumento adicional que
proporcionemos será accesible a través de las variables especiales
$1
, $2
, $3
, que se refieren al
primer, segundo y tercer argumento de línea de órdenes,
respectivamente.
Sabiendo esto, podemos usar argumentos adicionales para definir el
rango de líneas a pasar a head
y tail
respectivamente:
head -n "$2" "$1" | tail -n "$3"
Ahora podemos ejecutar:
SALIDA
ATOM 9 H 1 1.324 0.350 -1.332 1.00 0.00
ATOM 10 H 1 1.271 1.378 0.122 1.00 0.00
ATOM 11 H 1 -0.074 -0.384 1.288 1.00 0.00
ATOM 12 H 1 -0.048 -1.362 -0.205 1.00 0.00
ATOM 13 H 1 -1.183 0.500 -1.412 1.00 0.00
Cambiando los argumentos de nuestro comando, podemos cambiar el comportamiento de nuestro script:
SALIDA
ATOM 14 H 1 -1.259 1.420 0.112 1.00 0.00
ATOM 15 H 1 -2.608 -0.407 1.130 1.00 0.00
ATOM 16 H 1 -2.540 -1.303 -0.404 1.00 0.00
ATOM 17 H 1 -3.393 0.254 -0.321 1.00 0.00
TER 18 1
Esto funciona, pero puede que la próxima persona que lea
middle.sh
tarde un momento en darse cuenta de lo que hace.
Podemos mejorar nuestro script añadiendo algunos
comentarios en la parte superior:
# Select lines from the middle of a file.
# Usage: bash middle.sh filename end_line num_lines
head -n "$2" "$1" | tail -n "$3"
Un comentario empieza con un carácter #
y llega hasta el
final de la línea. El ordenador ignora los comentarios, pero tienen un
valor incalculable para ayudar a la gente (incluido tu futuro yo) a
entender y utilizar los scripts. La única advertencia es que cada vez
que modifiques el script, debes comprobar que el comentario sigue siendo
correcto. Una explicación que lleve al lector en la dirección equivocada
es peor que ninguna.
¿Qué pasa si queremos procesar muchos archivos en un solo proceso?
Por ejemplo, si queremos ordenar nuestros archivos .pdb
por
longitud, escribiríamos:
porque wc -l
lista el número de líneas en los ficheros
(recuerde que wc
significa ‘recuento de palabras’,
añadiendo la opción -l
significa ‘recuento de líneas’ en su
lugar) y sort -n
ordena las cosas numéricamente. Podríamos
poner esto en un fichero, pero entonces sólo ordenaría una lista de
ficheros .pdb
en el directorio actual. Si queremos obtener
una lista ordenada de otros tipos de ficheros, necesitamos una forma de
introducir todos esos nombres en el script. No podemos usar
$1
, $2
, etc. porque no sabemos cuántos
ficheros hay. En su lugar, usamos la variable especial $@
,
que significa, ‘Todos los argumentos de línea de comandos del script de
shell’. También debemos poner $@
entre comillas dobles para
manejar el caso de argumentos que contienen espacios ("$@"
es sintaxis especial y es equivalente a "$1"
"$2"
…).
He aquí un ejemplo:
# Sort files by their length.
# Usage: bash sorted.sh one_or_more_filenames
wc -l "$@" | sort -n
SALIDA
9 methane.pdb
12 ethane.pdb
15 propane.pdb
20 cubane.pdb
21 pentane.pdb
30 octane.pdb
163 ../creatures/basilisk.dat
163 ../creatures/minotaur.dat
163 ../creatures/unicorn.dat
596 total
Lista de especies únicas
Leah tiene varios cientos de ficheros de datos, cada uno de los cuales tiene el siguiente formato:
2013-11-05,deer,5
2013-11-05,rabbit,22
2013-11-05,raccoon,7
2013-11-06,rabbit,19
2013-11-06,deer,2
2013-11-06,fox,1
2013-11-07,rabbit,18
2013-11-07,bear,1
Un ejemplo de este tipo de fichero se da en
shell-lesson-data/exercise-data/animal-counts/animals.csv
.
Podemos utilizar el comando
cut -d , -f 2 animals.csv | sort | uniq
para producir las
especies únicas en animals.csv
. Para evitar tener que
teclear esta serie de comandos cada vez, un científico puede optar por
escribir un script de shell en su lugar.
Escribe un script de shell llamado species.sh
que tome
cualquier número de nombres de archivo como argumentos de línea de
comandos y utilice una variación del comando anterior para imprimir una
lista de las especies únicas que aparecen en cada uno de esos archivos
por separado.
Supongamos que acabamos de ejecutar una serie de comandos que han hecho algo útil, por ejemplo, crear un gráfico que nos gustaría utilizar en un artículo. Nos gustaría poder volver a crear el gráfico más tarde si lo necesitamos, así que queremos guardar los comandos en un archivo. En lugar de escribirlos de nuevo (y potencialmente equivocarnos) podemos hacer lo siguiente:
El fichero redo-figure-3.sh
contiene ahora:
297 bash goostats.sh NENE01729B.txt stats-NENE01729B.txt
298 bash goodiff.sh stats-NENE01729B.txt /data/validated/01729.txt > 01729-differences.txt
299 cut -d ',' -f 2-3 01729-differences.txt > 01729-time-series.txt
300 ygraph --format scatter --color bw --borders none 01729-time-series.txt figure-3.png
301 history | tail -n 5 > redo-figure-3.sh
Después de un momento de trabajo en un editor para eliminar los
números de serie en los comandos, y para eliminar la línea final donde
llamamos al comando history
, tenemos un registro
completamente exacto de cómo creamos esa figura.
¿Por qué registrar los comandos en el historial antes de ejecutarlos?
Si ejecutas el comando:
el último comando del archivo es el propio comando
history
, es decir, el shell ha añadido history
al registro de comandos antes de ejecutarlo realmente. De hecho, el
shell siempre añade comandos al registro antes de ejecutarlos.
¿Por qué crees que lo hace?
Si un comando hace que algo se bloquee o se cuelgue, puede ser útil saber cuál era ese comando para investigar el problema. Si el comando sólo se registrara después de ejecutarlo, no tendríamos un registro del último comando ejecutado en caso de que se produjera un bloqueo.
En la práctica, la mayoría de la gente desarrolla secuencias de
comandos ejecutándolas unas cuantas veces para asegurarse de que están
haciendo lo correcto y guardándolas en un archivo para poder
reutilizarlas. Este estilo de trabajo permite a la gente reciclar lo que
descubren sobre sus datos y su flujo de trabajo con una llamada a
history
y un poco de edición para limpiar la salida y
guardarla como un script de shell.
Nelle’s Pipeline: Creación de un script
El supervisor de Nelle insistió en que todos sus análisis debían ser reproducibles. La forma más sencilla de capturar todos los pasos es en un script.
Primero volvemos al directorio del proyecto de Nelle:
Crea un fichero usando nano
…
…que contiene lo siguiente:
BASH
# Calculate stats for data files.
for datafile in "$@"
do
echo $datafile
bash goostats.sh $datafile stats-$datafile
done
Guarda esto en un archivo llamado do-stats.sh
de modo
que ahora puede volver a hacer la primera etapa de su análisis
escribiendo:
También puede hacer esto:
para que la salida sea sólo el número de archivos procesados en lugar de los nombres de los archivos que fueron procesados.
Una cosa a tener en cuenta sobre el script de Nelle es que deja que la persona que lo ejecuta decida qué archivos procesar. Podría haberlo escrito como:
BASH
# Calculate stats for Site A and Site B data files.
for datafile in NENE*A.txt NENE*B.txt
do
echo $datafile
bash goostats.sh $datafile stats-$datafile
done
La ventaja es que siempre selecciona los archivos correctos: no tiene
que acordarse de excluir los archivos ‘Z’. La desventaja es que
siempre selecciona sólo esos archivos — no puede ejecutarlo en
todos los archivos (incluyendo los archivos ‘Z’), o en los archivos ‘G’
o ‘H’ que sus colegas en la Antártida están produciendo, sin editar el
script. Si quisiera ser más atrevida, podría modificar el script para
comprobar si hay argumentos en la línea de comandos y utilizar
NENE*A.txt NENE*B.txt
si no se proporciona ninguno. Por
supuesto, esto introduce otro compromiso entre flexibilidad y
complejidad.
Variables en Shell Scripts
En el directorio alkanes
, imagine que tiene un script de
shell llamado script.sh
que contiene los siguientes
comandos:
Mientras estás en el directorio alkanes
, escribe el
siguiente comando:
¿Cuál de los siguientes resultados esperaría ver?
- Todas las líneas entre la primera y la última línea de cada fichero
que termina en
.pdb
en el directorioalkanes
- La primera y la última línea de cada fichero que termina en
.pdb
en el directorioalkanes
- La primera y la última línea de cada fichero en el directorio
alkanes
- Error debido a las comillas alrededor de
*.pdb
La respuesta correcta es 2.
Las variables especiales $1
, $2
y
$3
representan los argumentos de línea de comandos dados al
script, de tal forma que los comandos ejecutados son:
BASH
$ head -n 1 cubane.pdb ethane.pdb octane.pdb pentane.pdb propane.pdb
$ tail -n 1 cubane.pdb ethane.pdb octane.pdb pentane.pdb propane.pdb
El shell no expande '*.pdb'
porque está entre comillas.
Como tal, el primer argumento del script es '*.pdb'
que se
expande dentro del script por head
y tail
.
Encuentra el fichero más largo con una extensión dada
Escribe un script de shell llamado longest.sh
que tome
como argumentos el nombre de un directorio y una extensión de nombre de
archivo, e imprima el nombre del archivo con más líneas en ese
directorio con esa extensión. Por ejemplo:
imprimiría el nombre del fichero .pdb
en
shell-lesson-data/exercise-data/alkanes
que tiene más
líneas.
Siéntete libre de probar tu script en otro directorio p.e.
BASH
# Shell script which takes two arguments:
# 1. a directory name
# 2. a file extension
# and prints the name of the file in that directory
# with the most lines which matches the file extension.
wc -l $1/*.$2 | sort -n | tail -n 2 | head -n 1
La primera parte del proceso, wc -l $1/*.$2 | sort -n
,
cuenta las líneas de cada archivo y las ordena numéricamente (la más
grande en último lugar). Cuando hay más de un archivo, wc
también genera una línea de resumen final, dando el número total de
líneas en todos los archivos. Usamos
tail -n 2 | head -n 1
para desechar esta última línea.
Con wc -l $1/*.$2 | sort -n | tail -n 1
veremos la línea
de resumen final: podemos construir nuestro pipeline por partes para
estar seguros de que entendemos la salida.
Comprensión de lectura del script
Para esta pregunta, consideremos de nuevo el directorio
shell-lesson-data/exercise-data/alkanes
. Este contiene un
número de archivos .pdb
además de cualquier otro archivo
que pueda haber creado. Explique qué haría cada uno de los tres scripts
siguientes al ejecutarse como bash script1.sh *.pdb
,
bash script2.sh *.pdb
y bash script3.sh *.pdb
respectivamente.
En cada caso, el shell expande el comodín en *.pdb
antes
de pasar la lista resultante de nombres de archivo como argumentos al
script.
El script 1 imprimiría una lista de todos los archivos que contienen un punto en su nombre. Los argumentos pasados al script no se utilizan realmente en ninguna parte del script.
El script 2 imprimiría el contenido de los 3 primeros ficheros con
extensión .pdb
. los argumentos $1
,
$2
y $3
se refieren al primer, segundo y
tercer argumento respectivamente.
El script 3 imprimiría todos los argumentos del script (es decir,
todos los archivos .pdb
), seguidos de
.pdb
.$@
se refiere a todos los
argumentos dados a un script de shell.
SALIDA
cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb.pdb
Scripts de depuración
Suponga que ha guardado el siguiente script en un archivo llamado
do-errors.sh
en el directorio
north-pacific-gyre
de Nelle:
BASH
# Calculate stats for data files.
for datafile in "$@"
do
echo $datfile
bash goostats.sh $datafile stats-$datafile
done
Cuando lo ejecutas desde el directorio
north-pacific-gyre
:
la salida está en blanco. Para averiguar por qué, vuelva a ejecutar
el script utilizando la opción -x
:
¿Qué te muestra la salida? ¿Qué línea es la responsable del error?
La opción -x
hace que bash
se ejecute en
modo de depuración. Esto imprime cada comando a medida que se ejecuta,
lo que le ayudará a localizar errores. En este ejemplo, podemos ver que
echo
no imprime nada. Hemos cometido un error tipográfico
en el nombre de la variable de bucle, y la variable datfile
no existe, por lo que devuelve una cadena vacía.
Puntos Clave
- Guarda comandos en archivos (normalmente llamados shell scripts) para reutilizarlos.
-
bash [filename]
ejecuta los comandos guardados en un archivo. -
$@
se refiere a todos los argumentos de línea de comandos de un script de shell. -
$1
,$2
, etc., se refieren al primer argumento de la línea de comandos, al segundo argumento de la línea de comandos, etc. - Ponga las variables entre comillas si los valores pueden contener espacios.
- Dejar que los usuarios decidan qué archivos procesar es más flexible y más consistente con los comandos Unix incorporados.
Content from Encontrar cosas
Última actualización: 2025-04-03 | Mejora esta página
Hoja de ruta
Preguntas
- ¿Cómo puedo encontrar los archivos?
- ¿Cómo puedo encontrar cosas en archivos?
Objetivos
- Utilice
grep
para seleccionar líneas de archivos de texto que coincidan con patrones simples. - Utilice
find
para buscar archivos y directorios cuyos nombres coincidan con patrones sencillos. - Utilizar la salida de un comando como argumento(s) de otro comando.
- Explique qué se entiende por ficheros ‘de texto’ y ‘binarios’, y por qué muchas herramientas comunes no manejan bien estos últimos.
Del mismo modo que muchos de nosotros utilizamos ahora “Google” como verbo que significa “encontrar”, los programadores de Unix utilizan a menudo la palabra “grep”. grep” es una contracción de “global/regular expression/print”, una secuencia de operaciones habitual en los primeros editores de texto de Unix. También es el nombre de un programa de línea de comandos muy útil.
grep
encuentra e imprime las líneas de los archivos que
coinciden con un patrón. Para nuestros ejemplos, utilizaremos un archivo
que contiene tres haiku tomados de un concurso
de 1998 en la revista Salon (Crédito a los autores Bill
Torcaso, Howard Korder, y Margaret Segall, respectivamente. Véase Haiku
Error Messsages archivado Página
1 y Página
2 .). Para este conjunto de ejemplos, vamos a trabajar en el
subdirectorio de escritura:
SALIDA
The Tao that is seen
Is not the true Tao, until
You bring fresh toner.
With searching comes loss
and the presence of absence:
"My Thesis" not found.
Yesterday it worked
Today it is not working
Software is like that.
Busquemos líneas que contengan la palabra “no”:
SALIDA
Is not the true Tao, until
"My Thesis" not found
Today it is not working
Aquí, not
es el patrón que estamos buscando. El comando
grep busca en el fichero las coincidencias con el patrón especificado.
Para utilizarlo escriba grep
, luego el patrón que estamos
buscando y finalmente el nombre del archivo (o archivos) en el que
estamos buscando.
La salida son las tres líneas del fichero que contienen las letras “no”.
Por defecto, grep busca un patrón distinguiendo entre mayúsculas y minúsculas. Además, el patrón de búsqueda que hemos seleccionado no tiene por qué formar una palabra completa, como veremos en el siguiente ejemplo.
Busquemos el patrón: ‘The’.
SALIDA
The Tao that is seen
"My Thesis" not found.
Esta vez se muestran dos líneas que incluyen las letras “The”, una de las cuales contiene nuestro patrón de búsqueda dentro de una palabra más grande, “Thesis”.
Para restringir las coincidencias a las líneas que contengan la
palabra ‘El’ sola, podemos dar a grep
la opción
-w
. Esto limitará las coincidencias a los límites de las
palabras.
Más adelante en esta lección, también veremos cómo podemos cambiar el comportamiento de búsqueda de grep con respecto a su sensibilidad a mayúsculas y minúsculas.
SALIDA
The Tao that is seen
Tenga en cuenta que un “límite de palabra” incluye el principio y el
final de una línea, es decir, no sólo las letras rodeadas de espacios. A
veces no queremos buscar una sola palabra, sino una frase. También
podemos hacerlo con grep
poniendo la frase entre
comillas.
SALIDA
Today it is not working
Ya hemos visto que no es necesario entrecomillar las palabras sueltas, pero es útil utilizar comillas cuando se buscan varias palabras. También ayuda a distinguir más fácilmente entre el término o frase de búsqueda y el fichero buscado. Utilizaremos comillas en los ejemplos siguientes.
Otra opción útil es -n
, que numera las líneas que
coinciden:
SALIDA
5:With searching comes loss
9:Yesterday it worked
10:Today it is not working
Aquí podemos ver que las líneas 5, 9 y 10 contienen las letras “it”.
Podemos combinar opciones (es decir, banderas) como hacemos con otros
comandos Unix. Por ejemplo, busquemos las líneas que contienen la
palabra ‘the’. Podemos combinar la opción -w
para encontrar
las líneas que contienen la palabra ‘the’ y -n
para numerar
las líneas que coinciden:
SALIDA
2:Is not the true Tao, until
6:and the presence of absence:
Ahora queremos utilizar la opción -i
para que nuestra
búsqueda no distinga entre mayúsculas y minúsculas:
SALIDA
1:The Tao that is seen
2:Is not the true Tao, until
6:and the presence of absence:
Ahora, queremos utilizar la opción -v
para invertir
nuestra búsqueda, es decir, queremos que aparezcan las líneas que no
contienen la palabra ‘the’.
SALIDA
1:The Tao that is seen
3:You bring fresh toner.
4:
5:With searching comes loss
7:"My Thesis" not found.
8:
9:Yesterday it worked
10:Today it is not working
11:Software is like that.
Si utilizamos la opción -r
(recursiva),
grep
puede buscar un patrón recursivamente a través de un
conjunto de ficheros en subdirectorios.
Busquemos recursivamente Yesterday
en el directorio
shell-lesson-data/exercise-data/writing
:
SALIDA
./LittleWomen.txt:"Yesterday, when Aunt was asleep and I was trying to be as still as a
./LittleWomen.txt:Yesterday at dinner, when an Austrian officer stared at us and then
./LittleWomen.txt:Yesterday was a quiet day spent in teaching, sewing, and writing in my
./haiku.txt:Yesterday it worked
grep
tiene muchas otras opciones. Para saber cuáles son,
podemos teclear:
SALIDA
Usage: grep [OPTION]... PATTERN [FILE]...
Search for PATTERN in each FILE or standard input.
PATTERN is, by default, a basic regular expression (BRE).
Example: grep -i 'hello world' menu.h main.c
Regexp selection and interpretation:
-E, --extended-regexp PATTERN is an extended regular expression (ERE)
-F, --fixed-strings PATTERN is a set of newline-separated fixed strings
-G, --basic-regexp PATTERN is a basic regular expression (BRE)
-P, --perl-regexp PATTERN is a Perl regular expression
-e, --regexp=PATTERN use PATTERN for matching
-f, --file=FILE obtain PATTERN from FILE
-i, --ignore-case ignore case distinctions
-w, --word-regexp force PATTERN to match only whole words
-x, --line-regexp force PATTERN to match only whole lines
-z, --null-data a data line ends in 0 byte, not newline
Miscellaneous:
... ... ...
Usando grep
¿Qué comando daría como resultado la siguiente salida:
SALIDA
and the presence of absence:
grep "of" haiku.txt
grep -E "of" haiku.txt
grep -w "of" haiku.txt
grep -i "of" haiku.txt
La respuesta correcta es 3, porque la opción -w
sólo
busca coincidencias de palabras completas. Las otras opciones también
coincidirán con “de” cuando forme parte de otra palabra.
Comodines
El verdadero poder de grep
no proviene de sus opciones,
sino del hecho de que los patrones pueden incluir comodines. (El nombre
técnico para éstos es expresiones regulares, que es lo
que significa la “re” en “grep”) Las expresiones regulares son complejas
y potentes; si quieres hacer búsquedas complejas, consulta la lección en
nuestro
sitio web. Como muestra, podemos encontrar líneas que tengan una ‘o’
en la segunda posición de la siguiente manera:
SALIDA
You bring fresh toner.
Today it is not working
Software is like that.
Usamos la opción -E
y ponemos el patrón entre comillas
para evitar que el shell intente interpretarlo. (Si el patrón contuviera
un *
, por ejemplo, el shell intentaría expandirlo antes de
ejecutar grep
) El ^
en el patrón ancla la
coincidencia al principio de la línea. El .
coincide con un
único carácter (igual que ?
en el shell), mientras que el
o
coincide con una o
real.
Seguimiento de una especie
Leah tiene varios cientos de archivos de datos guardados en un directorio, cada uno de los cuales tiene el siguiente formato:
2012-11-05,deer,5
2012-11-05,rabbit,22
2012-11-05,raccoon,7
2012-11-06,rabbit,19
2012-11-06,deer,2
2012-11-06,fox,4
2012-11-07,rabbit,16
2012-11-07,bear,1
Quiere escribir un script de shell que tome una especie como primer
argumento de la línea de comandos y un directorio como segundo
argumento. El script debe devolver un fichero llamado
<species>.txt
que contenga una lista de fechas y el
número de esa especie visto en cada fecha. Por ejemplo, utilizando los
datos mostrados anteriormente, rabbit.txt
contendría:
2012-11-05,22
2012-11-06,19
2012-11-07,16
A continuación, cada línea contiene un comando individual, o tubería. Ordene su secuencia en un comando para lograr el objetivo de Leah:
Sugerencia: use man grep
para buscar como grep texto
recursivamente en un directorio y man cut
para seleccionar
más de un campo en una línea.
En
shell-lesson-data/exercise-data/animal-counts/animals.csv
se proporciona un ejemplo de un archivo de este tipo
grep -w $1 -r $2 | cut -d : -f 2 | cut -d , -f 1,3 > $1.txt
En realidad, se puede cambiar el orden de los dos comandos de corte y todavía funciona. En la línea de comandos, intente cambiar el orden de los comandos de corte, y eche un vistazo a la salida de cada paso para ver por qué este es el caso.
Llamarías al script de arriba así:
Mujercitas
Tú y tu amiga, que acabáis de terminar de leer Mujercitas de
Louisa May Alcott, estáis discutiendo. De las cuatro hermanas del libro,
Jo, Meg, Beth y Amy, tu amiga piensa que Jo era la más mencionada. Tú,
sin embargo, estás segura de que fue Amy. Por suerte, tienes un fichero
LittleWomen.txt
que contiene el texto completo de la novela
(shell-lesson-data/exercise-data/writing/LittleWomen.txt
).
Utilizando un bucle for
, ¿cómo tabularías el número de
veces que se menciona a cada una de las cuatro hermanas?
Sugerencia: una solución puede emplear los comandos grep
y wc
y un |
, mientras que otra puede utilizar
las opciones grep
. A menudo hay más de una manera de
resolver una tarea de programación, por lo que una solución particular
se elige generalmente sobre la base de una combinación de producir el
resultado correcto, la elegancia, la legibilidad y la velocidad.
for sis in Jo Meg Beth Amy
do
echo $sis:
grep -ow $sis LittleWomen.txt | wc -l
done
Solución alternativa, ligeramente inferior:
for sis in Jo Meg Beth Amy
do
echo $sis:
grep -ocw $sis LittleWomen.txt
done
Esta solución es inferior porque grep -c
sólo informa
del número de líneas coincidentes. El número total de coincidencias
informado por este método será menor si hay más de una coincidencia por
línea.
Los observadores más perspicaces se habrán dado cuenta de que los
nombres de los personajes a veces aparecen en mayúsculas en los títulos
de los capítulos (por ejemplo, “MEG GOES TO VANITY FAIR”). Si quiere
contarlos también, puede añadir la opción -i
para no
distinguir entre mayúsculas y minúsculas (aunque en este caso, no afecta
a la respuesta sobre qué hermana se menciona con más frecuencia).
Mientras que grep
busca líneas en ficheros, el comando
find
busca ficheros en sí. De nuevo, tiene muchas opciones;
para mostrar cómo funcionan las más simples, usaremos el árbol de
directorios shell-lesson-data/exercise-data
que se muestra
a continuación.
SALIDA
.
├── animal-counts/
│ └── animals.csv
├── creatures/
│ ├── basilisk.dat
│ ├── minotaur.dat
│ └── unicorn.dat
├── numbers.txt
├── alkanes/
│ ├── cubane.pdb
│ ├── ethane.pdb
│ ├── methane.pdb
│ ├── octane.pdb
│ ├── pentane.pdb
│ └── propane.pdb
└── writing/
├── haiku.txt
└── LittleWomen.txt
El directorio exercise-data
contiene un fichero,
numbers.txt
y cuatro directorios:
animal-counts
, creatures
, alkanes
y writing
que contienen varios ficheros.
Para nuestro primer comando, vamos a ejecutar find .
(recuerde ejecutar este comando desde la carpeta
shell-lesson-data/exercise-data
).
SALIDA
.
./writing
./writing/LittleWomen.txt
./writing/haiku.txt
./creatures
./creatures/basilisk.dat
./creatures/unicorn.dat
./creatures/minotaur.dat
./animal-counts
./animal-counts/animals.csv
./numbers.txt
./alkanes
./alkanes/ethane.pdb
./alkanes/propane.pdb
./alkanes/octane.pdb
./alkanes/pentane.pdb
./alkanes/methane.pdb
./alkanes/cubane.pdb
Como siempre, .
por sí solo significa el directorio de
trabajo actual, que es donde queremos que empiece nuestra búsqueda. la
salida de find
son los nombres de cada archivo
y directorio bajo el directorio de trabajo actual. Esto
puede parecer inútil al principio, pero find
tiene muchas
opciones para filtrar la salida y en esta lección descubriremos algunas
de ellas.
La primera opción de nuestra lista es -type d
que
significa ‘cosas que son directorios’. Efectivamente, la salida de
find
son los nombres de los cinco directorios (incluyendo
.
):
SALIDA
.
./writing
./creatures
./animal-counts
./alkanes
Observe que los objetos encontrados por find
no están
ordenados. Si cambiamos -type d
por -type f
,
obtendremos un listado de todos los ficheros:
SALIDA
./writing/LittleWomen.txt
./writing/haiku.txt
./creatures/basilisk.dat
./creatures/unicorn.dat
./creatures/minotaur.dat
./animal-counts/animals.csv
./numbers.txt
./alkanes/ethane.pdb
./alkanes/propane.pdb
./alkanes/octane.pdb
./alkanes/pentane.pdb
./alkanes/methane.pdb
./alkanes/cubane.pdb
Ahora probemos a buscar por nombre:
SALIDA
./numbers.txt
Esperábamos que encontrara todos los archivos de texto, pero sólo
imprime ./numbers.txt
. El problema es que el shell expande
caracteres comodín como *
antes de que se ejecuten
los comandos. Como *.txt
en el directorio actual se expande
a ./numbers.txt
, el comando que realmente ejecutamos
fue:
find
hizo lo que pedimos; sólo que pedimos la cosa
equivocada.
Para conseguir lo que queremos, hagamos lo que hicimos con
grep
: pongamos *.txt
entre comillas para
evitar que el shell expanda el comodín *
. De esta forma,
find
obtiene el patrón *.txt
, no el nombre de
fichero expandido numbers.txt
:
SALIDA
./writing/LittleWomen.txt
./writing/haiku.txt
./numbers.txt
Listado vs. Búsqueda
Se puede hacer que ls
y find
hagan cosas
similares dadas las opciones adecuadas, pero en circunstancias normales,
ls
lista todo lo que puede, mientras que find
busca cosas con ciertas propiedades y las muestra.
Como hemos dicho antes, el poder de la línea de comandos reside en la
combinación de herramientas. Hemos visto cómo hacerlo con tuberías;
veamos otra técnica. Como acabamos de ver,
find . -name "*.txt"
nos da una lista de todos los archivos
de texto dentro o debajo del directorio actual. ¿Cómo podemos combinar
eso con wc -l
para contar las líneas en todos esos
ficheros?
La forma más sencilla es poner el comando find
dentro de
$()
:
SALIDA
21022 ./writing/LittleWomen.txt
11 ./writing/haiku.txt
5 ./numbers.txt
21038 total
Cuando el shell ejecuta este comando, lo primero que hace es ejecutar
lo que hay dentro de $()
. Luego reemplaza la expresión
$()
con la salida de ese comando. Como la salida de
find
son los tres nombres de fichero
./writing/LittleWomen.txt
,
./writing/haiku.txt
, y ./numbers.txt
, el shell
construye el comando:
que es lo que queríamos. Esta expansión es exactamente lo que hace el
shell cuando expande comodines como *
y ?
,
pero nos permite usar cualquier comando que queramos como nuestro propio
‘comodín’.
Es muy común utilizar find
y grep
a la vez.
El primero busca ficheros que coincidan con un patrón; el segundo busca
líneas dentro de esos ficheros que coincidan con otro patrón. Aquí, por
ejemplo, podemos encontrar archivos txt que contengan la palabra
“searching” buscando la cadena ‘searching’ en todos los archivos
.txt
del directorio actual:
SALIDA
./writing/LittleWomen.txt:sitting on the top step, affected to be searching for her book, but was
./writing/haiku.txt:With searching comes loss
Correspondencia y sustracción
La opción -v
de grep
invierte la
coincidencia de patrones, de modo que sólo se imprimen las líneas que
no coinciden con el patrón. Teniendo esto en cuenta, ¿cuál de
los siguientes comandos encontrará todos los ficheros .dat en
creatures
excepto unicorn.dat
? Una vez hayas
pensado tu respuesta, puedes probar los comandos en el directorio
shell-lesson-data/exercise-data
.
find creatures -name "*.dat" | grep -v unicorn
find creatures -name *.dat | grep -v unicorn
grep -v "unicorn" $(find creatures -name "*.dat")
- Ninguna de las anteriores.
La opción 1 es correcta. Al poner la expresión de coincidencia entre
comillas se evita que el shell la expanda, por lo que se pasa al comando
find
.
La opción 2 también funciona en este caso porque el shell intenta
expandir *.dat
pero no hay ficheros *.dat
en
el directorio actual, así que la expresión comodín se pasa a
find
. Encontramos esto por primera vez en el episodio 3.
La opción 3 es incorrecta porque busca en el contenido de los ficheros las líneas que no coinciden con “unicornio”, en lugar de buscar en los nombres de los ficheros.
Archivos binarios
Nos hemos centrado exclusivamente en la búsqueda de patrones en archivos de texto. ¿Qué ocurre si sus datos están almacenados en imágenes, en bases de datos o en algún otro formato?
Un puñado de herramientas extienden grep
para manejar
algunos formatos que no son de texto. Pero un enfoque más generalizable
es convertir los datos en texto, o extraer los elementos similares al
texto de los datos. Por un lado, facilita las cosas sencillas. Por otro,
las cosas complejas suelen ser imposibles. Por ejemplo, es bastante
fácil escribir un programa que extraiga las dimensiones X e Y de
archivos de imagen para que grep
juegue con ellas, pero
¿cómo escribir algo para encontrar valores en una hoja de cálculo cuyas
celdas contengan fórmulas?
Una última opción es reconocer que el shell y el procesamiento de texto tienen sus límites, y utilizar otro lenguaje de programación. Cuando llegue el momento de hacerlo, no seas demasiado duro con el shell. Muchos lenguajes de programación modernos han tomado prestadas muchas ideas de él, y la imitación es también la forma más sincera de elogio.
El shell de Unix es más antiguo que la mayoría de las personas que lo utilizan. Ha sobrevivido tanto tiempo porque es uno de los entornos de programación más productivos jamás creados — quizás incluso el más productivo. Su sintaxis puede ser críptica, pero la gente que la domina puede experimentar con diferentes comandos de forma interactiva, y luego utilizar lo que han aprendido para automatizar su trabajo. Las interfaces gráficas de usuario pueden ser más fáciles de usar al principio, pero una vez aprendidas, la productividad del shell es insuperable. Y como escribió Alfred North Whitehead en 1911, ‘La civilización avanza ampliando el número de operaciones importantes que podemos realizar sin pensar en ellas’
- Buscar todos los ficheros con extensión
.dat
recursivamente desde el directorio actual - Cuente el número de líneas que contiene cada uno de estos ficheros
- Ordena numéricamente la salida del paso 2
Puntos Clave
-
find
encuentra archivos con propiedades específicas que coinciden con los patrones. -
grep
selecciona las líneas de los archivos que coinciden con los patrones. -
--help
es una opción soportada por muchos comandos bash, y programas que pueden ser ejecutados desde dentro de Bash, para mostrar más información sobre cómo usar estos comandos o programas. -
man [command]
muestra la página del manual de un comando determinado. -
$([command])
inserta la salida de un comando en su lugar.