TP 1. Introducción a Ubuntu, Bash y Programación
Slides (Parte 1) Slides (Parte 2) Resolución Ejercicio 3
Terminal online
Recursos Online
Objetivos
- Familiarizarse un poco con Ubuntu y su estructura de directorios
- Familiarizarse con el uso básico de los comandos de Bash
- Familiarizarse con los bloques lógicos básicos de la programación
Introducción al Tema
En este trabajo práctico vamos a aprender a usar la línea de comando de Ubuntu (también referida como terminal, consola o shell). Para muchos de nosotros, que estamos acostumbrados a la interfaz gráfica de sistemas operativos como los de Windows o GUI (por las siglas en ingles: Graphic User Interface), la línea de comando puede parecer un desafío, pero con práctica y algo de paciencia descubrirán que puede resultar amena. Su uso tiene dos ventajas destacables para nuestro campo:
- Nos permitirá trabajar en entornos o programas sin interfaz gráfica (GUI)
- Mediante el uso de programas o scripts, nos permitirá automatizar procesos, acelerando el trabajo y minimizando la cantidad de errores que podemos cometer con tareas repetitivas
¿Unix? ¿Linux? ¿Ubuntu?
Dependiendo que tan familiares estén con Linux, todos estos nombres pueden resultar un poco confusos. Vamos por partes:
-
Unix es un sistema operativo creado en 1969 por dos programadores estadounidenses que trabajaban para Bell Labs, una companía de investigación y desarrollo científico que en su momento era propiedad de AT&T (compañia estadounidense de teléfonos). Al ser un sistema operativo portable, multitarea y multiusuario se hizo rápidamente popular y se difundió por instituciones académicas y empresas
-
Debido a su popularidad, otros programadores quisieron hacer sus propias versiones de sistemas operativos basados en Unix, pero como sus sistemas operativos tenían código original de Unix, AT&T los demandó, paralizando esta tendencía
-
En 1983 se crea el proyecto GNU con el objetivo de crear un sistema operativo similar a Unix, pero gratis y de código abierto. GNU significa "GNU's Not Unix" (es un anagrama recursivo, los programadores se divierten barato). Hacia el fin de los 80s el proyecto ya tenía casi todos los programas que necesitaba, pero les faltaba conseguir un buen kernel (principal responsable de facilitar a los distintos programas acceso seguro al hardware de la computadora)
-
En 1991, Linus Torvalds empieza a crear lo que terminaría siendo Linux, un sistema operativo con su propio kernel que usaba muchas de los programas del proyecto GNU. Esta versión se volvió rápidamente la más popular de todas las versiones de GNU, llevando a que en 1993 se creara el Debian Project, un proyecto comunal con el objetivo de mejorar una distrubución de Linux que denominaron Debian GNU/Linux (también llamada simplemente Debian).
-
Ubuntu es una distribución de Linux creada en 2004 basada en Debian. Al día de la fecha es la distrubución más popular de Linux con más del 50% de los usuarios.
-
Lubuntu, que es el sistema operativo que usábamos en la máquina virtual, es una distribución de Linux creada en 2009 basada en Ubuntu. Es bastante similar a Ubuntu en todo lo que es consola, pero tiene una interfaz gráfica que consume menos recursos, haciéndolo ideal para máquinas más viejas (o en nuestro caso, máquinas virtuales que pesen lo menos posible).
Estructura de directorios de Ubuntu
La organización de archivos en Ubuntu y Lubuntu es bastante diferente a la de Windows. Si bien no vamos a detallar completamente toda la estructura y que es cada carpeta (porque el 95% no lo van a usar en esta materia), es importante tener una idea de lo básico:
- / Carpeta raiz, o root. Contiene al resto de las carpetas
- /etc Configuraciones del sistema para todos los usuarios (mucho cuidado al tocar)
- /home Ubicación de los directorios de los diferentes usuarios (o en este caso el único usuario)
- /home/ibioinfo Directorio del usuario ibioinfo. Es el lugar donde van a trabajar la mayoría del tiempo (incluye tanto el Escritorio como Documentos) y donde se abre por defecto la terminal (más sobre esto en un ratito). Comúnmente referida como home directory o home del usuario ibioinfo
- /media Si fuera una computadora normal (no VM) aca aparecerían los pendrives. En nuestro caso aca aparecen por defecto las carpetas compartidas con la PC host
- /tmp Ubicación de los archivos temporales de los programas
- /var Ubicación de los archivos variables de los programas, como logs, bases de datos, paginas webs, etc
- /var/log Probablemente la subcarpeta más usada de /var. Contiene los logs de los programas (que a veces es la única forma de saber porque algo no anduvo)
Esto es simplemente un vistazo rápido. Si quieren la lista completa de subdirectorios de Ubuntu la pueden encontrar en esta página, pero tengan en cuenta que tocar cualquier cosa fuera de /home conlleva la posibilidad de arruinar la computadora. En esta materia vamos a usar principalmente /home y /media.
Línea de comando
Como ya dijimos en la introducción la línea de comando tiene varios nombres, y en esta guia nos vamos a referir a ella como terminal o consola. Hay varias formas de abrir la terminal:
- Desde cualquier lado: Ctrl+Alt+T
- Desde cualquier lado: Inicio (menu de abajo a la izquierda) Herramientas del sistema Terminal
- Desde afuera de una carpeta: Boton derecho en la carpeta Abrir en el terminal
- Desde adentro de una carpeta: Herramientas Abrir la carpeta actual en un terminal (o apretar F4)
La terminal funciona como un explorador de archivos que se mueve entre las carpetas. Los primeros dos métodos van a abrir la terminal en /home/ibioinfo, mientras que los últimos dos métodos van a abrir la terminal en la carpeta elegida. Si en algun momento les decimos que abran la terminal y no aclaramos otra cosa nos referimos a abrirla en /home/ibioinfo.
Abran la terminal en /home/ibioinfo y deberian ver algo así:
ibioinfo@ibioinfo-VirtualBox:~$
Donde ibioinfo es el nombre del usuario actual e ibioinfo-VirtualBox el nombre de la computadora (que justo en este caso son similares, pero no es necesario). El ~ después de los dos puntos (conocido como "virgulilla", "tilde" o "cosito de la ñ") parece ser parte de la terminal, pero en realidad está indicando la carpeta en la que se encuentra en este momento. Como cada usuario trabaja mas que nada en su carpeta, Ubuntu le asigna el símbolo ~ a esa carpeta para simplificar los directorios que aparecen en la terminal. En nuestro caso ~ equivale a /home/ibioinfo y puede ser que nos refiramos a esa carpeta como su home directory o simplemente su home.
Info
Aclaraciones por si son fanáticos de los atajos de teclado:
- Para copiar texto en la terminal hay que usar Ctrl+Shift+C. En en resto de Ubuntu es normal (Ctrl+C).
- Para pegar texto en la terminal hay que usar Ctrl+Shift+V. En en resto de Ubuntu es normal (Ctrl+V).
- Al apretar Ctrl+C en la terminal le estan diciendo que corte forzosamente el programa que está corriendo. Si bien hay que tener cuidado con no cortar un proceso importante a la mitad, este atajo del teclado es útil si un programa se te quedó colgado o similar.
Bash: Ubicarse en la terminal
La terminal acepta una variedad de comandos en lenguaje Bash, que es el lenguaje de la terminal de GNU. El formato general de los comandos es:
comando -opciones parametro1 parametro2 etc
Donde comando es el nombre del programa a correr, opciones son comúnmente una o más letras luego de un guión que indican alguna modificación a las opciones por defecto del programa, y los diferentes parámetros son cosas que necesita el programa para correr, como puede ser un archivo que esta leyendo. Todo esto va a ir quedando más claro con los diferentes ejemplos.
Usted está aquí
Al ser la terminal básicamente un explorador de archivos, es necesario saber en que carpeta estoy y que hay adentro de dicha carpeta. pwd
es un comando que imprime el directorio actual en la terminal (P**rint **W**orking **D**irectory). Pruebenlo a ver si **~ era realmente /home/ibioinfo:
pwd
ls
:
ls
ls
va a colorear diferentes tipos de archivos (y de carpetas) de diferentes colores.
Este comando es una buena oportunidad de entender un poco mas sobre opciones y parámetros. Por defecto ls
lista los archivos de la carpeta actual, pero de darle un parámetro muestra los de dicha carpeta. Prueben correr lo siguiente:
ls /etc/perl/Net
Tip
El próximo comando es muy parecido al anterior. Pueden usar Up y Down en su terminal para navegar por los ultimos comandos utilizados y modificar lo necesario.
ls -l /etc/perl/Net
Donde la opción -l
agrega información sobre los permisos del archivo (quién puede leerlo o modificarlo). Con un poco de suerte esta es la primera y última vez en toda la materia que vamos a hablar de los permisos de Ubuntu (y de como a veces dan dolores de cabeza).
Info
La lista completa de las opciones para cada comando se puede ver con el comando man
(de manual). En este caso sería man ls
.
Moverse entre las carpetas
Ahora que ya saben dónde están y qué carpetas hay adentro es importante saber como moverse entre ellas. Esto se hace principalmente con el comando cd
.
Asegúrense que la terminal está en su home y usen ls
para ver nuevamente la lista de carpetas dentro. Primero vamos a la carpeta Escritorio y vemos que archivos hay adentro, para eso hacer:
Tip
Tab funciona como autocompletar en la consola. Al escribir el próximo comando prueben escribir solo cd Es y apretar Tab
cd Escritorio
Ahora verán que la parte de la izquierda de la terminal cambio a:
ibioinfo@ibioinfo-VirtualBox:~/Escritorio$
Indicando que están en /home/ibioinfo/Escritorio. Usen el comando adecuado para ver qué archivos hay adentro de esta carpeta.
Si quieren volver a su home y deben escribir:
cd ..
- ¿En qué carpeta están?
En todo lo que es Ubuntu ..
significa "una carpeta para arriba".
¿Qué pasa entonces si estan en /home/ibioinfo/Escritorio/TP01/Version3/Intento2/Edicion1 y quieren volver a su home? ¿Tienen que escribir cd ..
5 veces?
Técnicamente funciona, pero por defecto el comando cd
te lleva a tu home si no le das ningun parámetro.
cd
Paths relativos y absolutos
Cuando corrimos ls /etc/perl/Net
estaban ubicados en su home (/home/ibioinfo) y si vemos las carpetas dentro de home resulta que no existe ninguna llamada /etc.
- ¿Dónde está la carpeta /etc en relación a /home/ibioinfo? (ver Estructura de directorio de Ubuntu arriba si no se acuerdan)
- ¿Cómo pudimos acceder a /etc/perl/Net si la terminal estaba ubicada en una carpeta sin ninguna relación?
La respuesta a todo esto son los paths relativos y absolutos:
Paths absolutos
El path /etc/perl/Net es lo que se llama un path absoluto; no importa donde estén en la terminal en ese momento, /etc/perl/Net va a siempre apuntar al mismo lugar y el comando ls /etc/perl/Net
va a siempre andar bien.
Una forma fácil de identificar paths absolutos es que siempre empiezan en el root o /.
Recuerden que ~ apunta a /home/ibioinfo, y por lo tanto el comando cd ~/Escritorio
está usando un path absoluto, ya que sin importar de donde se use va a funcionar y va a ir a /home/ibioinfo/Escritorio.
Paths relativos
Ahora bien, cuando nosotros estabamos ubicados en home y corrimos cd Escritorio
pudimos entrar a /home/ibioinfo/Escritorio, pero si volviéramos a correr cd Escritorio
el comando no funcionaría, ya que no existe la carpeta /home/ibioinfo/Escritorio/Escritorio.
Esto se debe a que en este caso Escritorio es un path relativo a la ubicación actual de la terminal.
Otra forma de escribir paths relativos en Ubuntu es empezar con .
, simbolo que indica "la carpeta actual". Volviendo al ejemplo anterior, es equivalente escribir cd Escritorio
o cd ./Escritorio
.
Otro caso de path relativo que ya vimos es cd ..
, donde apunta a la "carpeta de arriba" de la posición actual de la terminal.
Ambos tipos de paths tienen sus ventajas y desventajas.
Los paths absolutos tienen la ventaja de funcionar siempre, pero al usar toda la estructura toman más tiempo de escribir y son más suceptibles a cambios de directorios (si muevo un archivo de lugar tengo que reescribir el comando).
Por otro lado, los paths relativos son mucho más rápidos de escribir y en muchos casos funcionan en diferentes ubicaciones (o computadoras), pero al depender de la ubicación de la terminal esto puede causar problemas si pienso que estoy en una carpeta pero estoy realmente en otra. En esta cursada vamos a usar ambos para diferentes casos.
Ejercicio 1. Ubicarse en la terminal
-
Identifique cuál o cuáles de los siguientes comandos te llevarían desde cualquier carpeta al home del usuario ibioinfo.
cd ~
cd home/ibioinfo
cd home / ibioinfo
cd / home / ibioinfo
cd /home/ibioinfo
cd /ibioinfo/home
cd ././ibioinfo
cd ./home/ibioinfo
-
¿Cambiarían la respuesta en algunos de los puntos anteriores si el usuario logueado actualmente en la computadora no es ibioinfo? ¿Por qué?
-
Identifique en la siguiente lista cuales paths son paths relativos:
/var/temp/tom_jerry
var/temp/tom_jerry
/home/tom/Documentos/catfood.png
../../jerry/Documentos/cheese.png
./Videos/Capitulos/
~/Videos/Capitulos/
./Descargas/tom_jerry_cap1.torrent
/home/tom/Descargas/tom_jerry_cap1.torrent
Bash: Crear y eliminar
Todo lo que es crear, copiar, mover y eliminar archivos y directorios se puede hacer usando la interfaz gráfica como lo harían en cualquier otro sistema operativo, sin embargo hay situaciones (por ejemplo dentro de un script) donde es necesario hacerlo mediante la consola. Si bien por ahora les vamos a pedir que usen los siguientes comandos para practicarlos, en el día a día hagan lo que les sea más cómodo.
Crear y eliminar directorios
Los directorios se pueden crear con:
mkdir FOLDER
Info
En el resto de la guía van a aparecer ciertas palabras en mayúscula en los códigos, como por ejemplo FOLDER. Estas palabras son variables o placeholders que tienen que ser reemplazadas por lo que corresponda.
Donde FOLDER es un path absoluto o relativo con el nombre de la carpeta. Prueben ir en su terminal a /home/ibioinfo/Documentos y usar el comando:
mkdir testfolder
Y vean si efectivamente apareció una carpeta nueva. Luego usen el comando de nuevo a ver que pasa.
Para eliminar directorios se puede usar:
rmdir FOLDER
Por defecto este comando sólo puede eliminar directorios vacíos (lo cual puede no ser muy útil, pero a la vez es seguro).
Usen el comando anterior y borren la carpeta que acaban de crear (reemplacen FOLDER por lo que corresponda). Prueben correr el comando una vez más a ver que pasa.
Tip muy importante
Este es un buen momento para hablar de que nombres ponerles a las cosas que uno crea un Ubuntu. Si bien cualquier nombre funciona en un principio, por un tema de compatibilidad entre los diferentes programas que pueden llegar a usar se recomienda:
- Muy recomendado: No usar comillas dobles, simples o apóstrofes
- Recomendado: No usar espacios, paréntesis, Ñ, acentos, diéresis u otros diacríticos (el espacio comúnmente se remplaza por un guión o guión bajo)
Crear y eliminar archivos
Hay varias formas de crear archivos en Ubuntu y la mas simple es touch ARCHIVO
, que crea un archivo de texto vacio donde ARCHIVO es el nombre del archivo (que puede incluir un path antes).
- Para entender lo que quiero decir vayan a su home y corran:
touch Documentos/testfile
Noten que el archivo que crearon no tiene extensión (por ejemplo .txt). En Ubuntu van a ver muchos archivos de texto sin extensión, pero se la pueden agregar sin problema si quieren. Entren a Documentos y vean si el archivo realmente existe.
Lo que le vamos a enseñar a continuación es probablemente uno de los comandos más peligrosos de Bash si se usa incorrectamente.
rm
es el comando usado para eliminar archivos (o carpetas, o discos enteros) y es la base de cientos de historias en internet de como alguien se quedó sin trabajo. El comando se usa:
rm ARCHIVO
Donde ARCHIVO es el archivo a eliminar. rm
no les va a pedir confirmación y el archivo va a ser borrado permanentemente (si borran archivos desde la interfaz gráfica sí hay confirmación y sí van a la papelera).
- Con cuidado, asegúrense que están en Documentos y borren el archivo testfile.
Danger
¿Se entendió que hay que tener cuidado con rm
? ¿Si? Buenísimo!
Mover y copiar archivos y carpetas
Adentro de Documentos creen una carpeta llamada testfolder2 y dos archivos de texto vacío, uno llamado testfile_mv y otro testfile_cp.
Nuestro objetivo va a ser mover testfile_mv a la carpeta testfolder2 y copiar testfile_cp a la misma carpeta.
Los archivos se mueven con mv
y se copian con cp
y ambos tienen un formato similar que es
mv ARCHIVO_ORIGEN FOLDER_DESTINO
Donde en este caso ARCHIVO_ORIGEN es el archivo a mover y FOLDER_DESTINO el path a donde moverlo.
- Prueben mover testfile_mv y copiar testfile_cp adentro de la carpeta testfolder2.
Tip
Usando la opción -i
al comando, les va a pedir confirmación si el archivo de destino ya existe. Recuerden que pueden usar man mv
(mv --help
) o man cp
(cp --help
) para ver más opciones.
Estos dos comandos también pueden ser usados como:
mv ARCHIVO_ORIGEN ARCHIVO_DESTINO
En este caso ARCHIVO_DESTINO no es una carpeta, sino un archivo adentro del FOLDER_DESTINO, lo que permite renombrar el ARCHIVO_ORIGEN al copiarlo / moverlo.
- Para que se entienda mejor este uso, ubíquense en Documentos y corran:
cp testfile_cp testfolder2/testfile_cp_nuevo
Con este comando copiaron testfile_cp de nuevo a la carpeta testfolder2, pero ahora con otro nombre. Interesantemente, usar mv
de esta manera es la forma de Ubuntu de renombrar archivos desde la terminal.
- Prueben entrar a testfolder2 y corran:
mv testfile_cp_nuevo testfile_cp_otroNombre
-
¿Qué pasó?
-
Si todo salió bien, usen
rm
para eliminar todos los testfile uno a uno y luego usenrmdir
para eliminar testfolder2.
Ambos comandos funcionan también para copiar, mover y renombrar carpetas, en cuyo caso el formato es:
mv FOLDER_ORIGEN FOLDER_DESTINO
Danger
Hay que ser cuidadosos al usar mv
y cp
ya que si el archivo de destino ya existe lo van a sobreescribir sin preguntar antes.
A ambos comandos se le puede agregar la opción -i
para que pida confirmación antes de sobreescribir si ya existiera el archivo de destino.
Bash: Archivos de texto
Escribir archivos de texto
En Ubuntu hay varias formas de escribir en archivos de texto, pero una de las más útiles para nosotros va a ser el >
que redirige la salida de información de la consola. Se usa:
comando -opciones parametro1 parametro2 > ARCHIVO_DESTINO
Donde lo de la izquierda de >
es el comando como lo correrías normalmente y ARCHIVO_DESTINO es un archivo donde va a ser guardada la salida de ese comando (lo que normalmente verían en la consola).
- Para entender un poco más, vayan a su home y corran:
ls -l > Documentos/output_de_ls
Verán que en un principio parece que no paso nada.
- Entren ahora a Documentos y van a ver que hay un nuevo archivo con el nombre output_de_ls.
Vamos a abir el archivo usando el Text Editor, el editor de texto de la interfaz ǵráfica de Ubuntu.
- Asegurándose que están en Documentos, y si están en la terminal local en la consola escriban:
gedit output_de_ls
Van a ver que se abre el editor de texto de igual forma que si ubieran hecho doble click en el ícono en el explorador de archivos de la GUI. Puede ser que aparezca un warning o advertencia en la consola, pero la podemos ignorar.
- Si están trabajando en replit abran el archivo haciendo click en el nombre del archivo en la barra lateral izquierda.
Tanto en gedit como en el replit, agreguen una nueva línea abajo de todo (con cualquier texto) y guarden el archivo (en el caso de replit se guarda automáticamente).
Ya sabemos como guardar en un archivo de texto cualquier salida de un comando de Ubuntu! pero... ¿cómo hacemos para poner lo que nosotros queremos en un archivo de texto? Simple, ¡con otro comando de Ubuntu!
El comando echo
hace lo que su nombre indica y devuelve por la terminal el texto que le pases.
- Prueben escribir
echo TEXTO
, donde TEXTO es cualquier oración, por ejemplo:
echo Probando, uno, dos, tres
Tal vez ya se dieron cuenta, pero combinando echo
con >
podemos escribir nuestros propios archivos de texto desde la terminal.
- Asegurándose que están adentro de Documentos, corran:
echo Esta es la primera línea del documento > mi_documento
-
Confirmen que se escribió el archivo y que tiene el texto adentro.
-
¿Qué piensan que pasa si ahora corro el siguiente comando?
echo Quiero agregar otra linea al documento > mi_documento
- Abran el documento
mi_documento
.
La primera línea que agregamos desapareció. Esto es porque cada uso de >
sobreescribe el archivo. Si queremos agregar otra línea a un documento que ya tiene información tenemos que usar el comando >>
que agrega el texto al archivo en una nueva línea al final sin modificar el contenido anterior.
- Así que ahora que sabemos esto podemos correr estos dos comandos:
echo Esta es la primera linea del documento > mi_documento
echo Esta es la segunda linea del documento >> mi_documento
- Abran el archivo y vean si funcionó como queríamos.
Danger
Hay que ser cuidadosos al usar >
ya que si el archivo de destino ya existe lo va a sobreescribir sin preguntar antes.
Leer archivos de texto
Qué comando usar al leer archivos de texto en la consola depende mucho de que tan largo es el archivo y que me interesa de él:
-
¿Tiene solo pocas lineas de texto?
cat
va a abrir el archivo e imprimirlo en la terminal. -
¿Tiene muchas líneas de texto y quiero ver las primeras páginas a ver de que se trata?
less
va a abrir el archivo y mostrar solo el texto que entra en la terminal. Aprentando Space pasa a la próxima página y apretando Q deja de leerlo. -
¿Tiene muchas líneas de texto y quiero ver solo las primeras líneas?
head
te muestra las primeras 10 líneas del archivo. Se puede especificar la cantidad de líneas agregando una opción, por ejemplo,head -3
muestra solo las primeras 3 líneas. -
¿Tiene muchas líneas de texto y quiero ver solo las últimas líneas?
tail
te muestra las últimas 10 líneas del archivo. Este número se puede cambiar de la misma forma que parahead
. -
¿No saben qué tan largo es un archivo dado?
Pueden averiguarlo con el comando
wc
, que devuelve el número de líneas, palabras y letras (en ese orden) en el archivo. De pasarle la opción-l
, el comando devuelve solo el número de líneas.
Todos estos funcionan de la forma:
comando ARCHIVO
Usando el archivo martin_fierro que se encuentra en los materiales del TP (botón al principio de todo), prueben los 5 comandos anteriores.
Buscar palabras en archivos de texto
Va a ser común cuando trabajemos con tablas que nos interese encontrar filas con cierto valor y una forma rápida de hacer eso es usar grep
, comando al que le pasas una palabra o patrón y busca filas dentro de un archivo que contengan dicha palabra o patrón.
En formato general el comando es:
grep PALABRA ARCHIVO
Usando el archivo martin_fierro con el que trabajamos en la sección anterior podemos correr:
grep cantar martin_fierro
Y vamos a ver todas las líneas del documento donde aparece la palabra "cantar". Hay mucho para hablar sobre grep
, pero por ahora lo que nos va a importar es:
- La opción
-v
devuelve las líneas que no contienen PALABRA - La opción
-c
devuelve el número de líneas que contienen PALABRA - Es posible pasarle varias opciones a un programa. De hacer
grep -v -c
voy a estar contando el número de líneas que no contengan PALABRA (el orden de las opciones no afecta el comportamiento)
Info
grep
también funciona con patrones, quienes son conocidos como Expresiones Regulares, o RegEx. Este tema es complejo y ya tenemos bastante que procesar, por lo tanto, no vamos a profundizar más sobre ellos en este momento. Pero... explicaremos cualquier patrón o expresión regular que usemos en la materia cuando aparezca.
Combinar comandos
En Bash es posible combinar comandos, lo que quiere decir pasarle la salida de un comando directamente como entrada a otro comando. Esto se hace dividiendo los diferentes comandos con | (o pipe).
Para entender un poco mejor veamos un ejemplo.
Digamos que quiero ver cuáles de las primeras 10 líneas del archivo martin_fierro contienen la palabra cantar. Para esto tengo que hacer:
head -20 martin_fierro | grep cantar
Fijense que en este caso parecería que a grep
no le estoy pasando ningun ARCHIVO. grep
usa como entrada la salida de head
. De esta forma se puede concatenar cualquier número de comandos que serán ejecutados de izquierda a derecha.
Ejercicio 2. Archivos de texto
Para este ejercicio vamos a seguir usando el archivo martin_fierro.
-
¿Cuántas líneas tiene el archivo?
-
¿Cuántas líneas contienen la palabra "cantar"?
-
¿Cuántas líneas no contienen la palabra "guitarra"?
-
Cree otro archivo de texto llamado martin_fierro_sinA con las líneas del archivo martin_fierro que no tengan la letra "a".
-
Sin borrar el contenido y usando la consola, agregue una línea al final de martin_fierro_sinA que indique el autor del Martin Fierro (José Hernández).
-
Volviendo al archivo original, ¿cuántas líneas no contienen la letra "o" y sí contienen la letra "i"? (Tip: use
|
para encadenar comandos)
Bash: Programación y Scripts
Info
A continuación vamos a ver una pequeña introducción a la programación usando Bash como lenguaje. El objetivo de lo que sigue no es aprenderse de memoria las estructuras y si hay que poner una llave aca o dejar un espacio allá, sino entender la lógica detrás de la programación y como se pueden usar variables, condicionales y ciclos para obtener el resultado deseado.
Scripts
Los scripts de Bash son básicamente una lista de muchos de los comandos que nosotros corrimos en la terminal, pero escritos dentro de un archivo. Al ejecutar ese archivo todos los comandos escritos en él serán corridos uno a uno de arriba a abajo.
Para simplificar un poco la tarea y enfocarnos en lo que importa en esta sección, vamos a utilizar la interfaz gráfica de Ubuntu.
-
Vayan a Documentos y creen un archivo llamado primer_programa.sh
- En la computadora local: Botón derecho Crear nuevo... Archivo vacío.
- En replit: Botón derecho en la barra lateral izquierda New File Escribir el nombre del archivo.
Luego, abran el archivo en el Text Editor (doble click) y escriban lo siguiente:
echo "----------------"
echo "| Hello world! |"
echo "----------------"
- Guarden el archivo, muevanse en la terminal/consola a la carpeta donde está el script y lo ejecutan con:
bash primer_programa.sh
¡Felicidades, ya pueden decir que son programadores!
Como pudieron observar, los scripts de Bash se corren con el comando bash SCRIPT
y al hacerlo se ejecutaron los 3 comandos echo
en el orden que estaban dentro del script. Este tipo de scripts son útiles si quiero dejar evidencia de los comandos que corrí en Bash, ya sea para volver a hacerlo otro día o para pasarselos a alguien más y que los corra en su propia computadora; sin embargo, para realmente programar necesitamos más herramientas.
Variables
Las variables son palabras que guardan dentro de ellas un número o un string (texto), entre otro tipo de valores posibles que veremos más adelante. Veamos un ejemplo de como usar variables (vean ambas pestañas):
nombre="Unsamer"
echo "Hola $nombre, ¿todo bien?"
# Las lineas que empiezan con # son comentarios, no afectan el código y sirven para aclarar que estas
# haciendo en tu programa o script
# Al declarar una variable en Bash no se puede poner espacio entre la variable, el = y el valor
# Las comillas se usan para indicar que lo de adentro es una cadena de caracteres, o *string*
nombre="Unsamer"
# Cuando se usa la variable, se le agrega el prefijo $
echo "Hola $nombre, ¿todo bien?"
- ¿Qué piensan que va a pasar de correr este código en un script? Pruebenlo.
Info
Desgraciadamente Bash es muy estricto al momento de programar y perdona bastante poco (como por ejemplo el tema de tener un espacio más o menos). Más adelante vamos a usar el lenguaje R que va a ser una de nuestras principales herramientas al momento de analizar y graficar datos y es mucho más amigable.
En este momento pueden estar pensando que hubiera sido mucho más fácil poner solo echo "Hola Unsamer, ¿todo bien?"
y ahorrarme el tema de la variable. Tienen razón. Por ahora.
¿Se acuerdan de los parámetros de los comandos de Bash? Al pasarle parámetros a un script de Bash estos se asignan automaticamente a variables llamadas $1
, $2
, etc. Editemos ahora nuestro código anterior:
nombre=$1
echo "Hola $nombre, ¿todo bien?"
# $1 es el primer parámetro que se le pasa al script de Bash
# Le estoy asignando el valor de una variable a otra variable. $1 sigue existiendo, pero no la uso más
nombre=$1
# Podría usar $1 directamente aca, pero así se entiende mucho más lo que hace el código al leerlo
# (y para programas muy complicados esto es muy importante)
echo "Hola $nombre, ¿todo bien?"
Y ahora corran:
bash SCRIPT "NOMBRE"
Por si la versión genérica no queda claro, si el script se llamara saludo.sh y quiero conseguir el mismo resultado que antes habría que correr:
bash saludo.sh "Unsamer"
Info
Técnicamente si estoy pasando solo una palabra las comillas no son necesarias, pero si el string que estoy pasando tiene un espacio tengo que ponerlas sí o sí.
Hay bastante más para hablar de las variables. Existen muchos tipos más de variables, como:
- booleanos (variable que es verdadera o falsa),
- arreglos (o vectores)
- listas.
Otros lenguajes de programación hasta tienen variables más complejas que pueden almacenar tablas enteras. Sin embargo, lo que acabamos de aprender es la base y va a ser suficiente por ahora. Más información sobre las variables en Bash se puede ver en esta página.
Condicionales
Las variables son importantes, pero gran parte de la programación es controlar el "flujo" del programa, es decir, que un script haga algo más que simplemente ir de arriba a abajo ejecutando comandos. La primera herramienta que vamos a aprender para controlar el flujo del programa son los condicionales que permiten crear secciones de código que se van a ejecutar solo si se cumple (o no se cumple) una condición. Por ejemplo:
numero=$1
echo "$numero es un número"
if (($numero > 10))
then
echo "$numero es mayor a 10"
fi
# Igual que antes estoy agarrando un parámetro al correr el script
numero=$1
echo "$numero es un número"
# *if* es la estructura más usada para condicionales.
# Adentro de los dobles paréntesis va la condición.
# > es el comparador, o sea, estamos preguntando si $numero es mayor que 10
if (($numero > 10))
then
# El codigo entre *then* y *fi* solo si ejecuta si la condición es verdad, de otra forma se saltea
# Este codigo esta más a la derecha, o *indentado*. Esto se hace con tab y en la mayoría de los lenguajes
# es solo para entender más fácil el código
echo "$numero es mayor a 10"
fi
# *fi* indica donde termina el condicional
- Copien este código a un script y prueben pasarle números menores y mayores a 10 a ver que pasa.
Info
Es importante remarcar que la condición del if (lo que en este caso se encuentra entre los corchetes) es básicamente una pregunta que puede tener sólo una de dos respuestas posibles: Sí (llamada en programación Verdadero o True) ó No (llamada en programación Falso o False)
Recuerden que a un condicional se le puede poner también que pase algo cuando no es verdad, por ejemplo:
numero=$1
echo "$numero es un número"
if (($numero > 10))
then
echo "$numero es mayor a 10"
else
echo "$numero es menor o igual a 10"
fi
numero=$1
echo "$numero es un número"
if (($numero > 10))
then
# Ahora si la condición es verdad se va a ejecutar el código entre *then* y *else* y luego va a
# seguir a partir de *fi*
echo "$numero es mayor a 10"
else
# El código entre *else* y *fi* se ejecuta solo cuando la condición no es verdad
echo "$numero es menor o igual a 10"
fi
Hay muchos más comparadores para usar con los condicionales if y son diferentes si estoy comparando números o strings. Se puede poner más de una condición por if y hay otras estructuras, como los case, que cumplen una función similar. Sin embargo, la base que aprendieron hoy es suficiente por ahora.
Más información sobre los condicionales en Bash, incluyendo una lista más detallada de los comparadores, se puede ver en esta página y en esta página (en Bash las condiciones pueden estar rodeadas por paréntesis o corchetes y en cada caso los comparadores se comportan diferente, ojo con esto).
Ciclos
Si quieren imprimir los números del 1 al 10 en la consola, tendrían que hacer echo 1
, echo 2
, etc, hasta llegar a echo 10
. ¿Qué pasa si ahora les pido del 1 al 100, o al 1000?. Por suerte, existen los ciclos.
Los ciclos son estructuras que nos permiten repetir algo varias veces y al usar variables podemos hacer que cada vez sea ligeramente diferente a la anterior.
for ((i=1;i<=1000;i++))
do
echo $i
done
# *for* es una de las estructuras más usadas para hacer ciclos
# *i* es el nombre de la variable que va a cambiar de valor en cada ciclo. Se le podria poner cualquier nombre a
# ésta variable, por ejemplo *numero* en nuestro caso, pero es costumbre ponele *i*
# i=1 indica que el primer valor de $i es 1
# i<=1000 indica que el ciclo se va a repetir mientras $i sea menor o igual a 1000
# i++ indica que al final de cada ciclo el valor de $i va a subir en 1
for ((i=1;i<=1000;i++))
do
# El código entre *do* y *done* se va a ejecutar una vez para cada posible $i en el rango
echo $i
done
Hay otra versión del for que comunmente se denomina for each. En este caso $i
no representa números que aumentan, sino diferentes elementos en una lista. Por ejemplo:
for color in rojo amarillo verde
do
echo "Este es el color $color"
done
# Como ahora la variable son elementos de una lista le pongo el nombre *color* para que se sepa que es,
# pero podría ser *i*
# Esta es una forma bastante mala de usar listas de elementos, donde la estoy declarando en el mismo *for*;
# comunmente las listas existen de antes en el programa o las obtengo de un archivo o comando de Ubuntu
for color in rojo amarillo verde
do
echo "Este es el color $color"
done
Hay otros dos tipos de ciclos comúnmente denominados while y until (también llamado do) y hay formas de forzar salir del ciclo o pasar a la próxima iteración con break y continue (tambien llamado next).
Nuevamente, la base aprendida será suficiente por ahora. Más información sobre los ciclos en Bash se puede ver en esta página.
Ejercicio 3. Programación en Bash
El objetivo de este ejercicio es hacer un script que:
- Use por lo menos un for y un if
- Recorra los números del 1 al 10
- Por cada uno de esos números cree un archivo llamado archivo_NUMERO, dónde hay que reemplazar NUMERO por el número correspondiente (de 1 a 10)
- En los primeros 5 archivos (archivo_1 a archivo_5) escriba el texto:
Donde hay que reemplazar NUMERO por el número correspondiente (de 1 a 5)
Primera parte. Este es el archivo NUMERO.
- En los últimos 5 archivos (archivo_6 a archivo_10) escriba el texto:
Donde hay que reemplazar NUMERO por el número correspondiente (de 6 a 10). Noten que ambas oraciones están en lineas diferentes
Segunda parte. Este es el archivo NUMERO.
Ahora que sabemos nuestro objetivo vayan a Documentos y creen una nueva carpeta donde vamos a trabajar llamada TP01_EJ3. Dentro de ella creen un archivo vacío llamado crear_archivos.sh que va a ser nuestro script y edítenlo.
Guía para hacer el script
Al momento de hacer programas complejos, especialmente en un lenguaje que recién aprenden, es recomendado ir por partes e ir probando en el medio. Unos posibles pasos a seguir son:
Info
La idea de hacerlo así es ir probando de a poco si aparece algun error. ¡Prueben el script entre cada paso!
-
Modifiquen el script para que cree un archivo llamado archivo_1 que adentro tenga el texto:
Primera parte. Este es el archivo 1.
-
Agreguen un for que vaya de 1 a 5 y cree los archivos archivo_1 a archivo_5 que adentro tengan el texto:
Primera parte. Este es el archivo NUMERO.
Donde hay que reemplazar NUMERO por el número correspondiente (de 1 a 5).
-
Expandan el for para que vaya de 1 a 10. Agreguen un if adentro del for que haga que los archivos se creen solo para los primeros 5 ciclos.
Tip
Aca les puede venir bien el comparador
<=
, que significa "menor o igual". Un ejemplo de<=
seria:if (($1 <= 7))
Que en este caso es verdadero cuando el parámetro
$1
es menor o igual a 7. -
Agreguen un else al if, recordando que los comandos adentro del else se van a ejecutar cuando la condición no sea verdadera. Asumiendo que usaron
<=
en la condición del if, modifiquen los comandos adentro del else para que en ese caso se creen los archivos archivo_6 a archivo_10 que adentro tengan el texto:Segunda parte. Este es el archivo NUMERO.
Donde hay que reemplazar NUMERO por el número correspondiente (de 6 a 10).
¡Y listo, deberían tener su programa andando!
Ejercicios Adicionales
Info
Algunas guías van a tener ejercicios adicionales, que son ejercicios que pueden hacer si quieren practicar más el tema, pero no son obligatorios. Estos ejercicios pueden llegar a ser un poco más complicados que los ejercicios de la guía.
Ejercicio Adicional 1. Bash: Tablas
Lo último que vamos a aprender hoy es un pequeño vistazo a como se pueden manipular tablas desde la consola de Ubuntu. Descarguen el archivo mtcars que se encuentra en los materiales del TP (boton al principio de todo) y ponganlo en Documentos. Esta tabla viene por defecto con el lenguaje de programación R y nos va a servir para aprender como manipular tablas en Bash.
Abran el archivo. Podemos ver que es una tabla en formato texto, donde la primera línea es el encabezado o header de la tabla y en cada líńea las columnas están separadas entre ellas con un Tab (a estos archivos se los conoce como TSV o "Tab-Separated Values"). Es bioinformática es muy común querer seleccionar columnas específicas en una tabla, o filtrar filas debido al valor de una de sus columnas; esto es lo que vamos a aprender a continuación.
Columnas de mtcars
mtcars es una tabla que viene por defecto con el lenguaje de programación R, sus columnas son:
Nombre | Descripción |
---|---|
car_name | Name of the car |
mpg | Miles/(US) gallon |
cyl | Number of cylinders |
disp | Displacement (cu.in.) |
hp | Gross horsepower |
drat | Rear axle ratio |
wt | Weight (1000 lbs) |
qsec | ¼ mile time |
vs | Engine (0 = V-shaped, 1 = straight) |
am | Transmission (0 = automatic, 1 = manual) |
gear | Number of forward gears |
carb | Number of carburetors |
AWK
El comando awk
(que recibe su nombre de las iniciales de los apellidos de las 3 personas que lo crearon) es uno de los comandos mas usados en Bash para manipular tablas por su gran flexibilidad, hasta el punto que es posible incorporar condicionales y ciclos dentro de él. La forma más simple del comando es:
awk -opciones 'instrucciones' ARCHIVO_TABLA
Donde ARCHIVO_TABLA es el archivo que contiene a la tabla e instrucciones es que hacer con ese archivo una vez que se abra (las instrucciones siempre tienen que estar delimitadas por comillas simples, o '
). Colocando a nuestra terminal en Documentos podemos correr:
awk -F "\t" '{print}' mtcars
Donde -F
es la opción que le dice a awk
que caracter separa las diferentes columnas (en este caso es \t
, que es el símbolo de Tab) y {print}
es la instrucción que simplemente dice que imprima en pantalla la tabla.
Lo importante de awk
es que nos permite trabajar con columnas individuales. Por ejemplo si ponemos:
awk -F "\t" '{print $1}' mtcars
Vemos que awk
imprime solo la primera columna, que en este caso es el nombre de los autos. Podemos asumir entonces que cada columna se puede referir con $1
, $2
, etc. Probemos imprimir muchas columnas corriendo:
awk -F "\t" '{print $1 $3 $5}' mtcars
- ¿Qué ven que pasa acá?
Debido a como funciona print
, las diferentes columnas se imprimeron una pegada a la otra sin dejar espacios. Si quisiéramos imprimir las columnas separadas con Tab como la tabla original tenemos que hacer:
awk -F "\t" '{print $1 "\t" $3 "\t" $5}' mtcars
Una tarea común cuando se tienen tablas con muchos datos es filtrar los datos por alguna columna, o dicho de otra forma, usar condicionales. Un ejemplo de esto en awk
sería:
awk -F "\t" '{if ($3 == 6) {print}}' mtcars
- ¿Qué les parece que hace ese comando? Piensen que estan viendo una estructura de if que no vieron antes, pero aún así probablemente puedan inferir que va a hacer el comando pensando en como funcionaba el if de Bash que aprendimos arriba. Esto es super normal en la programación, donde la estructura exacta cambia, pero la lógica detrás se mantiene constante.
Entonces, acá le dicen a awk
que imprima en la pantalla todas las filas que tengan un valor de 6 en la columna 3 (que si se fijan es cyl, o el número de cilindros).
awk
tiene su propio grep
y su propio for, que se pueden declarar variables dentro de él y que tiene hasta una lista de comandos propios. Pueden ver mucha más información de awk
en esta página.
Ejercicio Adicional 2. Programación en Bash v2
El objetivo de este Ejercicio va a ser hacer un script que:
- Reciba un número por consola (vamos a asumir que dicho número va a ser siempre un número entero entre 1 y 1000) es decir que el usuario ingrese como parámetro del script un número.
- Recorra todos los números entre 1 y el número que recibió por consola
- Para cada uno de esos números vea si es par
- Imprima los números pares por consola
Dos cosas que van a necesitar para hacer esto son:
# El operador % calcula el resto entre 2 números
# En este caso $resto va a contener el resto de dividir 5 por 2 (que es 1)
# Una forma muy usada en programacíon para ver si un número es par es ver si su resto al dividirlo por 2 es 0
# Los paréntesis y signo $ bordeando a la operación son necesarios para que funcione bien en Bash
resto=$((5 % 2))
# == es el comparador para igualdad usado en los *ifs*
# Va a ser verdadero solo si lo de la izquierda es identico a lo de la derecha.
if (($1 == 2))
Para hacer este ejercicio pueden usar como base el código creado en el Ejercicio 3 que ambos tienen una estructura general bastante similar.
Bibliografía
Consola
- Comando
man