Comments

May 3, 2025

Cómo instalar y configurar LXD en Ubuntu 20.04

Introducción

Un contenedor Linux es un conjunto de procesos independiente del resto del sistema. Para el usuario final, un contenedor Linux funciona como una máquina virtual, pero es mucho más ligero. No tiene la sobrecarga de ejecutar un kernel Linux adicional y los contenedores no requieren compatibilidad con la virtualización de hardware de CPU. Esto significa que puede crear más contenedores que máquinas virtuales en el mismo servidor.

Imagina que tienes un servidor que debe ejecutar varios sitios web para tus clientes. Por un lado, cada sitio web podría ser un bloque de host/servidor virtual de la misma instancia del servidor web Apache o Nginx. Por otro lado, al usar máquinas virtuales, crearías una máquina virtual anidada independiente para cada sitio web. Los contenedores Linux se encuentran entre los hosts virtuales y las máquinas virtuales.

LXD te permite crear y administrar estos contenedores. LXD proporciona un servicio de hipervisor para gestionar todo el ciclo de vida de los contenedores. En este tutorial, configurarás LXD y lo usarás para ejecutar Nginx en un contenedor. Después, dirigirás el tráfico de internet al contenedor para que una página web de ejemplo sea accesible.

Prerrequisitos

Para completar este tutorial, necesitarás lo siguiente:

  • Un servidor con Ubuntu 20.04 . Para configurar un servidor, incluyendo un usuario sudo no root y un firewall, puede crear un Droplet de IsnHosting con Ubuntu 20.04 y luego seguir nuestra Guía de configuración inicial del servidor . Anote la dirección IP pública de su servidor. Más adelante la llamaremos your_server_ip.
  • Al menos 5 GB de almacenamiento en bloque. Para configurarlo, puede seguir la Guía rápida de volúmenes de almacenamiento en bloque de IsnHosting. En la configuración del almacenamiento en bloque, seleccione Manually Format & Mount[para permitir que LXD lo prepare según sea necesario]. Esto se utilizará para almacenar todos los datos relacionados con los contenedores.

Nota: A partir de Ubuntu 20.04, LXD está disponible oficialmente como paquete snap. Este nuevo formato de paquete ofrece varias ventajas. Un paquete snap se puede instalar en cualquier distribución de Linux compatible con este tipo de paquetes . Se recomienda usar un servidor con al menos 2 GB de RAM al ejecutar el paquete snap de LXD. La siguiente tabla resume las características del paquete snap de LXD:

Característicapaquete instantáneo
versiones LXD disponibles2.0, 3.0, 4.0, 4.x
requisitos de memoriaModerado, para el servicio SnapD . Servidor recomendado con 2 GB de RAM.
Consideraciones de actualizaciónPuede aplazar la actualización de LXD hasta 60 días
Capacidad de actualizar desde otro formato de paquetePuede actualizar de deb a snap.

Sigue el resto de este tutorial para usar LXD desde el paquete snap en Ubuntu 20.04. Si, por el contrario, quieres usar el paquete deb de LXD, consulta nuestro tutorial ” Cómo instalar y usar LXD en Ubuntu 18.04″ .

Paso 1: Preparación del entorno para LXD

Antes de configurar y ejecutar LXD, deberá preparar el entorno de su servidor. Esto implica agregar el usuario sudo al lxdgrupo y configurar el backend de almacenamiento.

Cómo agregar su cuenta no root al lxdgrupo Unix

Al configurar su cuenta no root, agréguela al lxdgrupo con el siguiente comando. El addusercomando toma como argumentos la cuenta de usuario y el grupo Unix para agregar la cuenta de usuario al grupo Unix existente:

sudo adduser sammy lxd

Ahora aplica la nueva membresía:

su sammy

Introduzca su contraseña y presione ENTER.

Por último, confirma que tu usuario ahora está agregado al lxdgrupo:

id -nG

Recibirás un resultado como este:

sammy sudo lxd

Ahora está listo para continuar configurando LXD.

Preparación del backend de almacenamiento

Para comenzar, configurará el backend de almacenamiento.

El sistema de almacenamiento recomendado para LXD en Ubuntu es el sistema de archivos ZFS. ZFS también funciona muy bien con el almacenamiento en bloque de IsnHosting . Para habilitar la compatibilidad con ZFS en LXD, primero actualice la lista de paquetes y luego instale el zfsutils-linuxpaquete auxiliar:

sudo apt update
sudo apt install -y zfsutils-linux

Estamos casi listos para ejecutar el script de inicialización de LXD.

Antes de hacerlo, debe identificar y tomar nota del nombre del dispositivo para su almacenamiento en bloque.

Para ello, utilice lspara comprobar el /dev/disk/by-id/directorio:

ls -l /dev/disk/by-id/

En este ejemplo específico, la ruta completa del nombre del dispositivo es /dev/disk/by-id/scsi-0DO_Volume_volume-fra1-0:

Outputtotal 0
lrwxrwxrwx 1 root root  9 Sep  16 20:30 scsi-0DO_Volume_volume-fra1-0 -> ../../sda

Anote la ruta completa del archivo de su dispositivo de almacenamiento. La usará en el siguiente paso al configurar LXD.

Paso 2: Inicialización y configuración de LXD

LXD está disponible como paquete snap en Ubuntu 20.04. Viene preinstalado, pero debes configurarlo.

Primero, verifique que el paquete snap de LXD esté instalado. El comando snap listmuestra los paquetes snap instalados:

snap list

Ubuntu 20.04 preinstala LXD 4.0.3 y está siguiendo el 4.0/stablecanal. LXD 4.0 tiene soporte durante cinco años (hasta 2025). Solo recibirá actualizaciones de seguridad:

Output of the "snap list" command — Listing the installed snap packagesName    Version   Rev    Tracking       Publisher   Notes
core18  20200724  1885   latest/stable  canonical✓  base
lxd     4.0.3     16922  4.0/stable/…   canonical✓  -
snapd   2.45.3.1  8790   latest/stable  canonical✓  snapd

Para obtener más información sobre el paquete snap instalado en LXD, ejecute snap info lxd. Podrá ver las versiones disponibles, incluyendo la fecha de la última actualización.

Ahora configurarás LXD.

Configuración de opciones de almacenamiento para LXD

Inicie el proceso de inicialización de LXD utilizando el sudo lxd initcomando:

sudo lxd init

Primero, el programa le preguntará si desea habilitar la agrupación en clústeres de LXD. Para este tutorial, presione ENTERpara aceptar el valor predeterminado noo escriba noy luego presione ENTER. La agrupación en clústeres de LXD es un tema avanzado que permite alta disponibilidad para su configuración de LXD y requiere al menos tres servidores LXD ejecutándose en un clúster:

OutputWould you like to use LXD clustering? (yes/no) [default=no]: no

Las siguientes seis indicaciones abordan el pool de almacenamiento. Proporcione las siguientes respuestas:

  • Presione ENTERpara configurar un nuevo grupo de almacenamiento.
  • Presione ENTERpara aceptar el nombre del grupo de almacenamiento predeterminado.
  • Presione ENTERpara aceptar el zfsbackend de almacenamiento predeterminado.
  • Presione ENTERpara crear un nuevo grupo ZFS.
  • Escriba yespara utilizar un dispositivo de bloque existente.
  • Por último, escriba la ruta completa al nombre del dispositivo de almacenamiento en bloque (esto es lo que registró anteriormente. Debería ser algo como: )./dev/disk/by-id/device_name

Sus respuestas se verán así:

OutputDo you want to configure a new storage pool? (yes/no) [default=yes]: yes
Name of the new storage pool [default=default]: default
Name of the storage backend to use (btrfs, dir, lvm, zfs) [default=zfs]: zfs
Create a new ZFS pool? (yes/no) [default=yes]: yes
Would you like to use an existing block device? (yes/no) [default=no]: yes
Path to the existing block device: /dev/disk/by-id/scsi-0DO_Volume_volume-fra1-01

Ya has configurado el backend de almacenamiento para LXD. Siguiendo con initel script de LXD, configurarás algunas opciones de red.

Configuración de opciones de red para LXD

LXD ahora pregunta si desea conectarse a un servidor MAAS (Metal As A Server). MAAS es un software que hace que un servidor físico parezca una máquina virtual y se gestione como tal.

Estamos ejecutando LXD en modo independiente, por lo tanto, acepte el valor predeterminado y responda no:

OutputWould you like to connect to a MAAS server? (yes/no) [default=no]: no

Se le pedirá que configure un puente de red para los contenedores LXD. Esto habilita las siguientes funciones:

  • Cada contenedor obtiene automáticamente una dirección IP privada.
  • Cada contenedor puede comunicarse con los demás a través de la red privada.
  • Cada contenedor puede iniciar conexiones a Internet.
  • De forma predeterminada, cada contenedor permanece inaccesible desde internet; no es posible iniciar una conexión desde internet y acceder a un contenedor a menos que se habilite explícitamente. Aprenderá a permitir el acceso a un contenedor específico en el siguiente paso.

Cuando se le solicite crear un nuevo puente de red local, elija yes:

OutputWould you like to create a new local network bridge? (yes/no) [default=yes]: yes

Luego acepta el nombre predeterminado lxdbr0:

OutputWhat should the new bridge be called? [default=lxdbr0]: lxdbr0

Acepte la selección automatizada del rango de direcciones IP privadas para el puente:

OutputWhat IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: auto
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: auto

Finalmente, LXD plantea las siguientes preguntas diversas:

Cuando se le pregunte si desea administrar LXD a través de la red, presione ENTERo responda no:

OutputWould you like LXD to be available over the network? (yes/no) [default=no]: no

Cuando se le pregunte si desea actualizar automáticamente las imágenes de contenedores obsoletos, presione ENTERo responda yes:

OutputWould you like stale cached images to be updated automatically? (yes/no) [default=yes] yes

Cuando se le pregunte si desea ver y conservar la configuración YAML que acaba de crear, responda yessi así lo desea. De lo contrario, presione ENTERo responda no:

OutputWould you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: no

Se ejecutará un script en segundo plano. Es normal que no se reciba ninguna salida.

Ya has configurado tus opciones de red y almacenamiento para LXD. A continuación, crearás tu primer contenedor LXD.

Paso 2: Creación y configuración de un contenedor LXD

Ahora que ha configurado LXD correctamente, está listo para crear y administrar su primer contenedor. En LXD, los contenedores se administran mediante el comando lxcseguido de una acción, como list, launch, y .startstopdelete

Utilice lxc listpara ver los contenedores instalados disponibles:

lxc list

Dado que esta es la primera vez que el lxccomando se comunica con el hipervisor LXD, muestra información sobre cómo iniciar un contenedor. Finalmente, el comando muestra una lista vacía de contenedores. Esto es normal, ya que aún no hemos creado ninguno.

Output of the "lxd list" commandTo start your first container, try: lxc launch ubuntu:18.04
+------+-------+------+------+------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+-------+------+------+------+-----------+

Ahora cree un contenedor que ejecute Nginx. Para ello, primero use el lxc launchcomando para crear e iniciar un contenedor de Ubuntu 18.04 llamado webserver.

Crea el webservercontenedor. ” 18.04in” ubuntu:18.04es un acceso directo para Ubuntu 18.04. ubuntu:Es el identificador del repositorio preconfigurado de imágenes LXD. También puedes usar ubuntu:bionic:

lxc launch ubuntu:20.04 webserver

Nota : Puede encontrar la lista completa de imágenes de Ubuntu disponibles ejecutando [<sub> lxc image list ubuntu:y</sub> otras distribuciones de Linux ejecutando [<sub>y</sub>] lxc image list images:. Tanto [<sub> ubuntu:y</sub> images:son repositorios de imágenes de contenedor. Para cada imagen de contenedor, puede obtener más información con el comando [<sub>y</sub>] lxc image info ubuntu:20.04.

Como es la primera vez que crea un contenedor, este comando descarga la imagen del contenedor de internet y la almacena en caché. Verá este resultado una vez que el nuevo contenedor termine de descargarse:

OutputCreating webserver
Starting webserver

Con el webservercontenedor iniciado, use el lxc listcomando para mostrar información sobre él. Hemos añadido ` --columns ns4para` para mostrar solo las columnas de name`,` statey IPv4`dirección`. El comando predeterminado lxc listmuestra tres columnas más: la dirección IPv6, si el contenedor es persistente o efímero, y si hay instantáneas disponibles para cada contenedor.

lxc list --columns ns4

La salida muestra una tabla con el nombre de cada contenedor, su estado actual, su dirección IP y su tipo:

Output+-----------+---------+------------------------------------+
|   NAME    |  STATE  |        IPV4                        |
+-----------+---------+------------------------------------+
| webserver | RUNNING | your_webserver_container_ip (eth0) |
+-----------+---------+------------------------------------+

El servidor DHCP de LXD proporciona esta dirección IP y, en la mayoría de los casos, se mantendrá igual incluso al reiniciar el servidor. Sin embargo, en los siguientes pasos, creará iptablesreglas para reenviar conexiones desde internet al contenedor. Por lo tanto, debe indicarle al servidor DHCP de LXD que siempre asigne la misma dirección IP al contenedor.

El siguiente conjunto de comandos configurará el contenedor para obtener una asignación de IP estática. Primero, anulará la configuración de red del eth0dispositivo heredada del perfil LXD predeterminado. Esto le permite establecer una dirección IP estática, lo que garantiza la correcta comunicación del tráfico web hacia y desde el contenedor.

Específicamente, lxc config devicees un comando que configconfigura un dispositivo device. La primera línea contiene la subacción overridepara anular el dispositivo eth0del contenedor webserver. La segunda línea contiene la subacción para configurar el ipv4.addresscampo del eth0dispositivo del webservercontenedor con la dirección IP proporcionada inicialmente por el servidor DHCP.

Ejecute el primer configcomando:

lxc config device override webserver eth0

Recibirás un resultado como este:

OutputDevice eth0 overridden for webserver

Ahora configure la IP estática:

lxc config device set webserver eth0 ipv4.address your_webserver_container_ip

Si el comando es exitoso, no recibirá ninguna salida.

Reiniciar el contenedor:

lxc restart webserver

Ahora verifique el estado del contenedor:

lxc list

Deberías ver que el contenedor es RUNNINGy la IPV4dirección es tu dirección estática.

Está listo para instalar y configurar Nginx dentro del contenedor.

Paso 3: Configuración de Nginx dentro de un contenedor LXD

En este paso te conectarás al webservercontenedor y configurarás el servidor web.

Conéctese al contenedor con lxc shellel comando, que toma el nombre del contenedor e inicia un shell dentro del contenedor:

lxc shell webserver

Una vez dentro del contenedor, el indicador de shell se verá así:

[environment second]  

Este shell, incluso si es un shell raíz, está limitado al contenedor. Todo lo que se ejecuta en él permanece en el contenedor y no puede escapar al servidor host.

Nota: Al introducir un shell en un contenedor, es posible que vea una advertencia como mesg: ttyname failed: No such device. Este mensaje se genera cuando el shell del contenedor intenta ejecutar el comando mesgdesde el archivo de configuración /root/.profile. Puede ignorarlo sin problema. Para evitarlo, puede eliminar el comando mesg n || truede /root/.profile.

Una vez dentro de su contenedor, actualice la lista de paquetes e instale Nginx:

apt update
apt install nginx

Con Nginx instalado, ahora editará la página web predeterminada de Nginx. En concreto, añadirá dos líneas de texto para que quede claro que este sitio está alojado en el webservercontenedor.

Usando nanosu editor preferido, abra el archivo /var/www/html/index.nginx-debian.html:

nano /var/www/html/index.nginx-debian.html

Añade las dos frases resaltadas al archivo:

/var/www/html/index.nginx-debian.html<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on LXD container webserver!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx on LXD container webserver!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
...

Editó el archivo en dos partes y agregó específicamente el texto on LXD container webserver. Guarde el archivo y salga del editor de texto.

Ahora cierre la sesión del contenedor:

logout

Una vez que aparezca el mensaje predeterminado del servidor, use [nombre del servidor] curlpara comprobar que el servidor web del contenedor funciona correctamente. Para ello, necesitará la dirección IP del contenedor web, que obtuvo con el lxc listcomando anterior.

Utilice curlpara probar su servidor web:

curl http://your_webserver_container_ip

Recibirá la página de bienvenida HTML predeterminada de Nginx. Tenga en cuenta que incluye sus modificaciones:

Output<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on LXD container webserver!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx on LXD container webserver!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
...

El servidor web funciona, pero solo se puede acceder a él desde el host usando la IP privada. En el siguiente paso, enrutará las solicitudes externas a este contenedor para que todo el mundo pueda acceder a su sitio web a través de internet.

Paso 4: Reenvío de conexiones entrantes al contenedor Nginx mediante LXD

Ahora que ha configurado Nginx, es hora de conectar el contenedor del servidor web a internet. Para comenzar, debe configurar el servidor para que reenvíe 80al webservercontenedor cualquier conexión que reciba en el puerto. Para ello, creará una iptablesregla para reenviar las conexiones de red. Puede obtener más información sobre IPTables en nuestros tutoriales ” Cómo funciona el firewall de IPtables” y “Fundamentos de IPtables: Reglas y comandos comunes del firewall” .

Este iptablescomando requiere dos direcciones IP: la dirección IP pública del servidor ( your_server_ip) y la dirección IP privada del webservercontenedor ( your_webserver_container_ip), que puede obtener mediante el lxc listcomando .

Ejecute este comando para crear una nueva regla de IPtables:

PORT=80 PUBLIC_IP=your_server_ip CONTAINER_IP=your_container_ip IFACE=eth0  sudo -E bash -c 'iptables -t nat -I PREROUTING -i $IFACE -p TCP -d $PUBLIC_IP --dport $PORT -j DNAT --to-destination $CONTAINER_IP:$PORT -m comment --comment "forward to the Nginx container"'

Estudiemos ese comando:

  • -t natespecifica que estamos usando la nattabla para la traducción de direcciones.
  • -I PREROUTINGespecifica que estamos agregando la regla a la cadena PREROUTING.
  • -i $IFACEespecifica la interfaz eth0, que es la interfaz de red pública predeterminada en el host para Droplets.
  • -p TCPdice que estamos usando el protocolo TCP.
  • -d $PUBLIC_IPEspecifica la dirección IP de destino para la regla.
  • --dport $PORT: especifica el puerto de destino (por ejemplo 80).
  • -j DNATdice que queremos realizar un salto a Destino NAT (DNAT).
  • --to-destination $CONTAINER_IP:$PORT dice que queremos que la solicitud vaya a la dirección IP del contenedor específico y al puerto de destino.

Nota: Puede reutilizar este comando para configurar reglas de reenvío. Restablezca las variables , y PORTal PUBLIC_IPprincipio de la línea. Simplemente cambie los valores resaltados.CONTAINER_IPIFACE

Ahora enumera tus reglas de IPTables:

sudo iptables -t nat -L PREROUTING

Verás un resultado como este:

[secondary_label Output] 
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       tcp  --  anywhere             your_server_ip       tcp dpt:http /* forward to this container */ to:your_container_ip:80
...

Ahora prueba que el servidor web sea accesible desde Internet.

Utilice el curlcomando desde su máquina local para probar las conexiones:

curl --verbose  'http://your_server_ip'

Verás los encabezados seguidos del contenido de la página web que creaste en el contenedor:

Output*   Trying your_server_ip...
* Connected to your_server_ip (your_server_ip) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.47.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: nginx/1.10.0 (Ubuntu)
...
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on LXD container webserver!</title>
<style>
    body {
...

Esto confirma que las solicitudes van al contenedor.

Por último, guardará la regla de firewall para que se vuelva a aplicar después de reiniciar.

Para ello, primero instale el iptables-persistentpaquete:

sudo apt install iptables-persistent

Al instalar el paquete, la aplicación le solicitará que guarde las reglas de firewall actuales. Acepte y guarde todas las reglas.

Al reiniciar el equipo, se cargará la regla del firewall. Además, el servicio Nginx del contenedor LXD se reiniciará automáticamente.

Has configurado LXD correctamente. En el paso final, aprenderás a detener y destruir el servicio.

Paso 5: Detención y retirada de contenedores mediante LXD

Puedes decidir que quieres desactivar el contenedor y eliminarlo. En este paso, lo detendrás y lo eliminarás.

Primero, detenga el contenedor:

lxc stop webserver

Utilice el lxc listcomando para verificar el estado:

lxc list

Verás que el estado del contenedor dice STOPPED:

Output+-----------+---------+------+------+------------+-----------+
|   NAME    |  STATE  | IPV4 | IPV6 |    TYPE    | SNAPSHOTS |
+-----------+---------+------+------+------------+-----------+
| webserver | STOPPED |      |      | PERSISTENT | 0         |
+-----------+---------+------+------+------------+-----------+

Para retirar el contenedor, utilice lxc delete:

lxc delete webserver

Al ejecutarlo lxc listnuevamente se muestra que no hay ningún contenedor ejecutándose:

lxc list

El comando generará el siguiente resultado:

+------+-------+------+------+------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+-------+------+------+------+-----------+

Utilice el lxc helpcomando para ver opciones adicionales.

Para eliminar la regla de firewall que enruta el tráfico al contenedor, primero ubique la regla en la lista de reglas con este comando, que asocia un número de línea con cada regla:

sudo iptables -t nat -L PREROUTING --line-numbers

Verás tu regla, precedida por un número de línea, de la siguiente manera:

OutputChain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination
1    DNAT       tcp  --  anywhere             your_server_ip      tcp dpt:http /* forward to the Nginx container */ to:your_container_ip

Utilice ese número de línea para eliminar la regla:

sudo iptables -t nat -D PREROUTING 1

Enumere nuevamente las reglas para garantizar la eliminación:

sudo iptables -t nat -L PREROUTING --line-numbers

Se elimina la regla:

OutputChain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination

Ahora guarde los cambios para que la regla no vuelva a aparecer cuando reinicie su servidor:

sudo netfilter-persistent save

Ahora puedes crear otro contenedor con tu propia configuración y agregar una nueva regla de firewall para reenviarle tráfico.

Conclusión

En este tutorial, instalaste y configuraste LXD. Luego, creaste un sitio web con Nginx, ejecutándose dentro de un contenedor LXD, y lo publicaste mediante IPtables.

Desde aquí, puedes configurar más sitios web, cada uno confinado en su propio contenedor, y usar un proxy inverso para dirigir el tráfico al contenedor correspondiente. El tutorial ” Cómo alojar varios sitios web con Nginx y HAProxy usando LXD en Ubuntu 16.04″ te guía en esta configuración.

Consulte la documentación de referencia de LXD para obtener más información sobre cómo utilizar LXD.

Para practicar con LXD, puedes probar LXD en línea y seguir el tutorial basado en la web.

Para obtener asistencia al usuario sobre LXD, visita el foro de discusión de LXD .

Stephen Dove
Comments

May 2, 2025

Cómo administrar paquetes en Ubuntu y Debian con Apt-Get y Apt-Cache

Introducción

Apt es una interfaz de línea de comandos para el sistema de empaquetado dpkg y es la forma preferida de gestionar software desde la línea de comandos en muchas distribuciones. Es el principal sistema de gestión de paquetes en Debian y distribuciones Linux basadas en Debian, como Ubuntu.

Mientras que una herramienta llamada “dpkg” constituye la capa de empaquetado subyacente, aptproporciona apt-cacheinterfaces intuitivas e implementa la gestión de dependencias. Esto permite a los usuarios gestionar grandes cantidades de software de forma eficiente y sencilla.

En esta guía, analizaremos el uso básico de apty apt-cachecómo pueden administrar su software. Practicaremos en un servidor en la nube Ubuntu 22.04, pero los mismos pasos y técnicas se aplican a cualquier otra distribución basada en Ubuntu o Debian.

Cómo actualizar la base de datos de paquetes con Apt

Apt opera con una base de datos de software conocido y disponible. Realiza instalaciones, búsquedas de paquetes y muchas otras operaciones consultando esta base de datos.

Por eso, antes de comenzar cualquier operación de empaquetado con apt, debemos asegurarnos de que nuestra copia local de la base de datos esté actualizada.

Actualice la base de datos local con apt update. Apt requiere privilegios administrativos para la mayoría de las operaciones:

sudo apt update

Verá una lista de los servidores de los que estamos recuperando información. Después de esto, su base de datos debería estar actualizada.

Cómo actualizar los paquetes instalados con Apt

Puede actualizar los paquetes de su sistema usando apt upgrade. Se le solicitará que confirme las actualizaciones y reinicie los servicios del sistema actualizados.

sudo apt upgrade

Cómo instalar nuevos paquetes con Apt

Si conoce el nombre de un paquete que necesita instalar, puede instalarlo usando apt install:

sudo apt install package1 package2 …

Puedes ver que es posible instalar varios paquetes a la vez, lo que es útil para adquirir todo el software necesario para un proyecto en un solo paso.

Apt instala no sólo el software solicitado, sino también cualquier software necesario para instalarlo o ejecutarlo.

Puedes instalar un programa llamado slescribiendo:

sudo apt install sl

Después de eso, podrás ejecutarlo slen la línea de comandos.

Cómo eliminar un paquete con Apt

Para eliminar un paquete de su sistema, ejecute apt remove:

sudo apt remove package_name

Este comando elimina el paquete, pero conserva los archivos de configuración por si lo vuelve a instalar más adelante. De esta forma, la configuración se mantendrá intacta, aunque el programa no esté instalado.

Si necesita limpiar los archivos de configuración y el programa, utilice apt purge:

sudo apt purge package_name

Esto desinstala el paquete y elimina cualquier archivo de configuración asociado con el paquete.

Para eliminar cualquier paquete que se instaló automáticamente para dar soporte a otro programa y que ya no sea necesario, escriba el siguiente comando:

sudo apt autoremove

También puede especificar un nombre de paquete después del autoremovecomando para desinstalar un paquete y sus dependencias.

Banderas de opciones comunes de Apt

Hay varias opciones adicionales que se pueden especificar mediante indicadores. Repasaremos algunas de las más comunes.

Para hacer un “ensayo” de un procedimiento para tener una idea de lo que hará una acción, puede pasar la -sbandera para “simular”:

sudo apt install -s htop

OutputReading package lists... Done
Building dependency tree... Done
Reading state information... Done
Suggested packages:
  lm-sensors
The following NEW packages will be installed:
  htop
0 upgraded, 1 newly installed, 0 to remove and 1 not upgraded.
Inst htop (3.0.5-7build2 Ubuntu:22.04/jammy [amd64])
Conf htop (3.0.5-7build2 Ubuntu:22.04/jammy [amd64])

En lugar de acciones reales, puede ver una sección Insty Confque especifica dónde se instalaría y configuraría el paquete si se eliminara el “-s”.

Si no desea que se le solicite que confirme sus opciones, también puede pasar la -ybandera para asumir automáticamente “sí” a las preguntas.

sudo apt remove -y htop

Si desea descargar un paquete, pero no instalarlo, puede ejecutar el siguiente comando:

sudo apt install -d packagename

Los archivos se conservarán en formato /var/cache/apt/archives.

Si desea suprimir la salida, puede pasar la -qqbandera al comando:

sudo apt remove -qq packagename

Cómo encontrar un paquete con Apt-Cache

La herramienta de empaquetado apt es en realidad un conjunto de herramientas complementarias relacionadas que se utilizan para administrar el software del sistema.

Mientras aptse utiliza para actualizar, instalar y eliminar paquetes, apt-cachese utiliza para consultar la base de datos de paquetes para obtener información sobre los paquetes.

Puede usar apt-cache searchpara buscar un paquete que se ajuste a sus necesidades. Tenga en cuenta que apt-cache no suele requerir privilegios de administrador:

apt-cache search what_you_are_looking_for

Por ejemplo, para encontrar htopuna versión mejorada del topmonitor del sistema, puede utilizar:

apt-cache search htop

Outputhtop - interactive processes viewer
aha - ANSI color to HTML converter
bashtop - Resource monitor that shows usage and stats
bpytop - Resource monitor that shows usage and stats
btop - Modern and colorful command line resource monitor that shows usage and stats
libauthen-oath-perl - Perl module for OATH One Time Passwords
pftools - build and search protein and DNA generalized profiles

También puedes buscar términos más genéricos. En este ejemplo, buscaremos software de conversión de MP3:

apt-cache search mp3 convert

Outputabcde - A Better CD Encoder
cue2toc - converts CUE files to cdrdao's TOC format
dir2ogg - audio file converter into ogg-vorbis format
easytag - GTK+ editor for audio file tags
ebook2cw - convert ebooks to Morse MP3s/OGGs
ebook2cwgui - GUI for ebook2cw
ffcvt - ffmpeg convert wrapper tool
. . .

Cómo ver la información del paquete con Apt-Cache

Para ver información sobre un paquete, incluida una descripción extendida, utilice la siguiente sintaxis:

apt-cache show package_name

Esto también proporcionará el tamaño de la descarga y las dependencias necesarias para el paquete.

Para ver si un paquete está instalado y verificar a qué repositorio pertenece, puede utilizar apt-cache policy:

apt-cache policy package_name

Conclusión

Ahora debería saber lo suficiente sobre apt-get y apt-cache para administrar la mayor parte del software en su servidor.

Si bien a veces es necesario ir más allá de estas herramientas y del software disponible en los repositorios, la mayoría de las operaciones de software pueden gestionarse mediante estas herramientas.

A continuación, podrá leer sobre la gestión de paquetes de Ubuntu y Debian en detalle.

Stephen Dove
Comments

May 2, 2025

Cómo usar el lenguaje AWK para manipular texto en Linux

Introducción

Las utilidades de Linux suelen seguir la filosofía de diseño de Unix. Se fomenta que las herramientas sean pequeñas, utilicen archivos de texto sin formato para la entrada y salida, y operen de forma modular. Gracias a este legado, disponemos de una gran funcionalidad de procesamiento de texto con herramientas como sed y awk.

awkEs un lenguaje de programación y procesador de texto que permite manipular datos textuales de forma muy útil. En esta guía, explorarás cómo usar la awkherramienta de línea de comandos y cómo procesar texto.

Sintaxis básica

El awkcomando está incluido de forma predeterminada en todos los sistemas Linux modernos, por lo que no es necesario instalarlo para comenzar a usarlo.

awkEs especialmente útil al gestionar archivos de texto con un formato predecible. Por ejemplo, es excelente para analizar y manipular datos tabulares. Opera línea por línea e itera por todo el archivo.

De forma predeterminada, se utilizan espacios en blanco (tabuladores, etc.) para separar los campos. Afortunadamente, muchos archivos de configuración de Linux utilizan este formato.

El formato básico de un awkcomando es:

awk '/search_pattern/ { action_to_take_on_matches; another_action; }' file_to_parse

Puede omitir la parte de búsqueda o la parte de acción de cualquier awkcomando. Por defecto, la acción que se realiza si no se especifica la parte “acción” es “imprimir”. Esto simplemente imprime todas las líneas que coinciden.

Si no se proporciona la parte de búsqueda, awkrealiza la acción que aparece en cada línea.

Si se proporcionan ambos, awkutiliza la parte de búsqueda para decidir si la línea actual refleja el patrón y luego realiza las acciones en función de las coincidencias.

En su forma más simple, puedes usar awklike catpara imprimir todas las líneas de un archivo de texto en la pantalla.

Crea un favorite_food.txtarchivo que enumere las comidas favoritas de un grupo de amigos:

echo "carrot sandy
wasabi luke
sandwich brian
salad ryan
spaghetti jessica" > favorite_food.txt

Ahora use el awkcomando para imprimir el archivo en la pantalla:

awk '{print}' favorite_food.txt

Verás el archivo impreso en la pantalla:

Outputcarrot sandy
wasabi luke
sandwich brian
salad ryan
spaghetti jessica

Esto no es muy útil. Probemos awklas funciones de filtrado de búsqueda buscando el texto “arena” en el archivo:

awk '/sand/' favorite_food.txt

Outputcarrot sandy
sandwich brian

Como puedes ver, awkahora solo imprime las líneas que tienen los caracteres “sand” en ellas.

Usando expresiones regulares, puedes seleccionar partes específicas del texto. Para mostrar solo la línea que empieza con las letras “sand”, usa la expresión regular ^sand:

awk '/^sand/' favorite_food.txt

Esta vez, solo se muestra una línea:

Outputsandwich brian

De igual forma, puede usar la sección de acción para especificar qué información desea imprimir. Por ejemplo, para imprimir solo la primera columna, use el siguiente comando:

awk '/^sand/ {print $1;}' favorite_food.txt

Outputsandwich

Puede referenciar cada columna (delimitada por espacios) mediante variables asociadas a su número de columna. Por ejemplo, la primera columna es $1, la segunda es $2, y puede referenciar toda la línea con $0.

Variables internas y formato expandido

El awkcomando utiliza algunas variables internas para asignar ciertas piezas de información mientras procesa un archivo.

Las variables internas que awkutiliza son:

  • NOMBRE DE ARCHIVO : Hace referencia al archivo de entrada actual.
  • FNR : Referencia el número del registro actual en relación con el archivo de entrada actual. Por ejemplo, si tiene dos archivos de entrada, esto le indicaría el número de registro de cada archivo en lugar del total.
  • FS : El separador de campo actual que se utiliza para identificar cada campo de un registro. Por defecto, se establece en espacios.
  • NF : El número de campos en el registro actual.
  • NR : El número del registro actual.
  • OFS : El separador de campo para los datos de salida. Por defecto, se establece en espacios.
  • ORS : El separador de registros para los datos de salida. Por defecto, es un carácter de nueva línea.
  • RS : El separador de registros utilizado para distinguir registros separados en el archivo de entrada. Por defecto, es un carácter de nueva línea.

Puede cambiar los valores de estas variables a voluntad para adaptarlas a las necesidades de sus archivos. Normalmente, esto se hace durante la fase de inicialización del procesamiento.

Esto nos lleva a otro concepto importante. La awksintaxis es un poco más compleja que la que has usado hasta ahora. También hay bloques opcionales BEGINque ENDpueden contener comandos para ejecutarse antes y después del procesamiento del archivo, respectivamente.

Esto hace que nuestra sintaxis expandida se vea más o menos así:

awk 'BEGIN { action; }
/search/ { action; }
END { action; }' input_file

Las palabras clave BEGINy ENDson conjuntos específicos de condiciones, al igual que los parámetros de búsqueda. Coinciden antes y después de procesar el documento.

Esto significa que puedes cambiar algunas de las variables internas de la BEGINsección. Por ejemplo, el /etc/passwdarchivo se delimita con dos puntos ( :) en lugar de espacios.

Para imprimir la primera columna de este archivo, ejecute el siguiente comando:

awk 'BEGIN { FS=":"; }
{ print $1; }' /etc/passwd

Outputroot
daemon
bin
sys
sync
games
man
. . .

Puede usar los bloques BEGINy ENDpara imprimir información sobre los campos que está imprimiendo. Use el siguiente comando para transformar los datos del archivo en una tabla, con tabulaciones bien espaciadas \t:

awk 'BEGIN { FS=":"; print "User\t\tUID\t\tGID\t\tHome\t\tShell\n--------------"; }
{print $1,"\t\t",$3,"\t\t",$4,"\t\t",$6,"\t\t",$7;}
END { print "---------\nFile Complete" }' /etc/passwd

Verás este resultado:

OutputUser		UID		GID		Home		Shell
--------------
root 		 0 		 0 		 /root 		 /bin/bash
daemon 		 1 		 1 		 /usr/sbin 		 /bin/sh
bin 		 2 		 2 		 /bin 		 /bin/sh
sys 		 3 		 3 		 /dev 		 /bin/sh
sync 		 4 		 65534 		 /bin 		 /bin/sync
. . .
---------
File Complete

Como puedes ver, puedes formatear las cosas bastante bien aprovechando algunas de awklas características de .

Cada sección expandida es opcional. De hecho, la sección de acción principal es opcional si se define otra sección. Por ejemplo, se pueden hacer cosas como esta:

awk 'BEGIN { print "We can use awk like the echo command"; }'

Y verás este resultado:

OutputWe can use awk like the echo command

Ahora veamos cómo buscar texto dentro de los campos de salida.

Búsqueda de campos y expresiones compuestas

En uno de los ejemplos anteriores, imprimiste la línea del favorite_food.txtarchivo que comenzaba con “sand”. Esto fue fácil porque buscabas el comienzo de toda la línea.

¿Qué sucedería si quisieras averiguar si un patrón de búsqueda coincide al principio de un campo ?

Cree una nueva versión del favorite_food.txtarchivo que agregue un número de artículo delante de la comida de cada persona:

echo "1 carrot sandy
2 wasabi luke
3 sandwich brian
4 salad ryan
5 spaghetti jessica" > favorite_food.txt

Si desea encontrar todos los alimentos de este archivo que comienzan con “sa”, puede comenzar probando algo como esto:

awk '/sa/' favorite_food.txt

Esto muestra todas las líneas que contienen “sa”:

Output1 carrot sandy
2 wasabi luke
3 sandwich brian
4 salad ryan

Aquí, se busca cualquier instancia de “sa” en la palabra. Esto termina incluyendo palabras como “wasabi”, que tiene el patrón en el medio, o “sandy”, que no está en la columna deseada. En este caso, solo se buscan palabras que empiecen por “sa” en la segunda columna.

Puedes indicar awkque solo coincida con el comienzo de la segunda columna usando este comando:

awk '$2 ~ /^sa/' favorite_food.txt

Como puedes ver, esto nos permite buscar una coincidencia solo al comienzo de la segunda columna.

La field_num ~parte especifica que awksólo se debe prestar atención a la segunda columna.

Output3 sandwich brian
4 salad ryan

Puedes buscar fácilmente elementos que no coincidan incluyendo el carácter “!” antes de la tilde (~). Este comando devolverá todas las líneas que no tengan un alimento que empiece por “sa”:

awk '$2 !~ /^sa/' favorite_food.txt

Output1 carrot sandy
2 wasabi luke
5 spaghetti jessica

Si más adelante decide que solo le interesan las líneas que no comienzan con “sa” y el número de elemento es menor que 5, puede usar una expresión compuesta como esta:

awk '$2 !~ /^sa/ && $1 < 5' favorite_food.txt

Esto introduce algunos conceptos nuevos. El primero es la posibilidad de añadir requisitos adicionales para que la línea coincida mediante el &&operador. Con este operador, se puede combinar cualquier número de condiciones para que la línea coincida. En este caso, se utiliza este operador para añadir una comprobación de que el valor de la primera columna sea menor que 5.

Verás este resultado:

Output1 carrot sandy
2 wasabi luke

Puedes usarlo awkpara procesar archivos, pero también puedes trabajar con la salida de otros programas.

Procesamiento de la salida de otros programas

Puedes usar el awkcomando para analizar la salida de otros programas en lugar de especificar un nombre de archivo. Por ejemplo, puedes usar awkpara analizar la dirección IPv4 del ipcomando.

El ip acomando muestra la dirección IP, la dirección de difusión y otra información sobre todas las interfaces de red de su equipo. Para mostrar la información de la interfaz llamada eth0, utilice este comando:

ip a s eth0 

Verás los siguientes resultados:

Output2571: eth0@if2572: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:0b brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.11/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

Puedes usarlo awkpara apuntar a la inetlínea y luego imprimir solo la dirección IP:

ip a s eth0 | awk -F '[\/ ]+' '/inet / {print $3}'

La -Fbandera indica awkque se debe delimitar con barras o espacios usando la expresión regular [\/ ]+. Esto divide la línea inet 172.17.0.11/16en campos separados. La dirección IP se encuentra en el tercer campo porque los espacios al principio de la línea también cuentan como un campo, ya que se delimitó con espacios además de barras. Tenga en cuenta que, awken este caso, los espacios consecutivos se tratan como un solo espacio.

La salida muestra la dirección IP:

Output172.17.0.11

Encontrarás muchos lugares donde puedes usar awkpara buscar o analizar la salida de otros comandos.

Conclusión

A estas alturas, deberías tener una comprensión básica de cómo usar el awkcomando para manipular, formatear e imprimir selectivamente archivos y secuencias de texto. Sin embargo, Awk es un tema mucho más amplio y, en realidad, es un lenguaje de programación completo con asignación de variables, estructuras de control, funciones integradas y más. Puedes usarlo en tus propios scripts para formatear texto de forma fiable.

Para obtener más información sobre awk, puede leer el libro gratuito de dominio público de sus creadores que brinda muchos más detalles.

Stephen Dove
Comments

May 2, 2025

Dominando el comando sed en Linux: una guía completa

Introducción

El sedcomando “Editor de Streams” en Linux es una potente utilidad para la manipulación de texto. Permite a los usuarios realizar diversas operaciones, como buscar, reemplazar, insertar y eliminar texto en archivos. Este tutorial ofrece una guía completa para dominar el sedcomando con ejemplos prácticos, explicaciones de sintaxis y casos de uso avanzados.

¿Qué es el comando sed?

El sedcomando es un editor de flujo que procesa texto línea por línea. Esto permite modificar el contenido del archivo sin abrirlo directamente en un editor de texto. Se usa ampliamente en scripts de shell y administración de sistemas para automatizar las tareas de procesamiento de texto.

Características principales de sed

1. Coincidencia y reemplazo de patrones 2. Edición de archivos en el lugar 3. Filtrado y manipulación de texto 4. Compatibilidad con expresiones regulares 5. Operaciones multilínea

Sintaxis básica del sed comando

La sintaxis básica del sedcomando consta de tres componentes principales: las opciones del comando, un script que define las instrucciones de edición y el archivo que se va a procesar.

Esta estructura permite a los usuarios especificar el comportamiento del comando, definir las transformaciones de texto y aplicarlas al archivo deseado.

  1. Opciones de comando : Se utilizan para especificar el comportamiento del comando. Por ejemplo, esta -iopción se utiliza para la edición in situ de archivos, es decir, para sobrescribirlos.
  2. Script : El script define las instrucciones de edición. Puede estar entre comillas simples ( ') o dobles ( "). Puede contener uno o más comandos de edición, separados por punto y coma ( ;).
  3. Archivo de entrada : Este es el archivo que se procesará. Puede ser un solo archivo o una lista de archivos separados por espacios. Si no se especifica ningún archivo, sedse lee desde la entrada estándar.

La sintaxis básica del sedcomando es la siguiente:

sed [options] 'script' file

En esta sintaxis, sedes el comando, [options]son las opciones del comando, 'script'contiene los comandos de edición y filees el archivo que se va a procesar.

sed [options] 'script' file

Lo entenderás mejor con los ejemplos que aparecen a continuación.

sed 's/hello/world/' sample.txt

Esto reemplaza la primera aparición de “hola” con “mundo” en cada línea de sample.txt.

Opciones de uso común ensed

OpciónDescripciónEjemplo
-iEdición in sitused -i 's/old/new/' file.txt
-nSuprimir la impresión automáticased -n '/pattern/p' file.txt
-eEjecutar múltiples comandossed -e 's/old/new/' -e '/pattern/d' file.txt
-fLeer comandos de un archivosed -f script.sed file.txt
-rUtilice expresiones regulares extendidassed -r 's/old/new/' file.txt
-EUtilice expresiones regulares extendidas (similares a -r)sed -E 's/old/new/' file.txt
-zSeparar líneas con carácter NULsed -z 's/old/new/' file.txt
-lEspecifique la longitud de línea para el comando ‘l’sed -l 100 'l' file.txt
-bModo binario (no elimine los caracteres CR)sed -b 's/old/new/' file.txt

Casos de uso más comunes desed

A continuación se muestran algunos de los casos de uso más prácticos del sedcomando.

Primero, creemos un archivo de texto de muestra file1.txty escribamos en él el siguiente texto, para facilitar la comprensión y el seguimiento.

cat > file1.txt

Copie y pegue el siguiente texto:

Linux is a family of free and open-source operating systems based on the Linux kernel.
Operating systems based on Linux are known as Linux distributions or distros.
Examples include Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Linux, and many others.

Buscar y reemplazar

En el siguiente comando, sse especifica la operación de sustitución y /son delimitadores. /Linux/Es el patrón de búsqueda y Unixes la cadena de reemplazo.

Nota: De forma predeterminada, el sedcomando solo reemplaza la primera aparición del patrón en cada línea y no reemplazará la segunda o tercera aparición en la línea.

sed 's/Linux/Unix/' file1.txt

Este comando reemplaza la primera aparición de “Linux” con “Unix” en cada línea.

Producción:

OutputUnix is a family of free and open-source operating systems based on the Linux kernel.
Operating systems based on Unix are known as Linux distributions or distros.
Examples include Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Unix, and many others.

Reemplazar globalmente en cada línea

La bandera de sustitución /g(reemplazo global) especifica el sedcomando para reemplazar todas las ocurrencias de la cadena en la línea.

sed 's/Linux/Unix/g' file1.txt

Este comando reemplaza todas las apariciones de “Linux” con “Unix” en cada línea.

Producción:

OutputUnix is a family of free and open-source operating systems based on the Unix kernel.
Operating systems based on Unix are known as Unix distributions or distros.
Examples include Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Unix, and many others.

Edición en el lugar

La opción `-i` permite la edición in situ del archivo. En pocas palabras, lo sobrescribe.

sed -i 's/Linux/Unix/' file1.txt

Este comando edita el archivo en su lugar, reemplazando “Linux” por “Unix” directamente en file1.txt. Sin -i, la inserción solo ocurre en la salida y no modifica el contenido del archivo. Para que el cambio sea persistente, debe usar la -iopción.

Eliminar líneas específicas

sed '2d' file1.txt

Este comando eliminará la segunda línea de file1.txt.

Producción:

OutputUnix is a family of free and open-source operating systems based on the Linux kernel.
Examples include Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Unix, and many others.

Suprime la -nimpresión automática del espacio de patrón y pes el comando de impresión.

sed -n '1,2p' file1.txt

Este comando imprime las líneas 1 a 2 de file1.txt.

Producción:

OutputUnix is a family of free and open-source operating systems based on the Unix kernel.
Operating systems based on Unix are known as Unix distributions or distros.

Eliminar líneas que coinciden con un patrón

Las /pattern/líneas coincidentes que contienen el patrón y la dbandera eliminan las líneas coincidentes.

sed '/kernel/d' file1.txt

Este comando eliminará todas las líneas que contengan la palabra “kernel”.

Producción:

OutputOperating systems based on Unix are known as Unix distributions or distros.
Examples include Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Unix, and many others.

Sustituir con un archivo de respaldo

El siguiente comando reemplazará todas las instancias de “Unix” por “Linux” y creará un archivo de respaldo con el file1.txt.baknombre del archivo anterior antes de reemplazarlo. Esto -i.bakpermite la edición in situ y crea un archivo de respaldo.

sed -i.bak 's/Unix/Linux/g' file1.txt

Producción:

OutputLinux is a family of free and open-source operating systems based on the Linux kernel.
Operating systems based on Linux are known as Linux distributions or distros.
Examples include Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Linux, and many others.

Y aquí está el contenido del archivo de respaldo file1.txt.bak.

more file1.txt.bak
OutputUnix is a family of free and open-source operating systems based on the Unix kernel.
Operating systems based on Unix are known as Unix distributions or distros.
Examples include Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Unix, and many others.

Reemplazar tabulaciones con espacios

El siguiente comando reemplazará cada tabulación con cuatro espacios. La \tbandera coincide con las tabulaciones y /gsirve para el reemplazo global en toda la línea.

sed 's/\t/    /g' file1.txt

Producción:

Output   Linux is a family of free and open-source operating systems based on the Linux kernel.
Operating systems based on Linux are known as Linux distributions or distros.
Examples     include Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Linux, and many others.

Eliminar líneas vacías

El siguiente comando eliminará todas las líneas vacías de file1.txt. El /^$comando “matches” elimina las líneas vacías y /dlas líneas coincidentes.

sed '/^$/d' file1.txt

Puede editar el file1.txtarchivo usando viel editor de texto y agregar algunas líneas vacías para probar este comando.

El siguiente comando imprime solo las líneas que contienen “Ubuntu”.

sed -n '/Ubuntu/p' file1.txt

La -nopción suprime la impresión automática. /Ubuntu/Coincide con las líneas que contienen el patrón y pse imprimen las líneas coincidentes.

Producción:

OutputExample includes Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Linux, and many others.

Casos de uso avanzados desed

Esta sección consta de algunos casos de uso avanzados y más complicados del sedcomando.

Insertar texto antes de una línea

El siguiente comando inserta “Este es el texto insertado.” antes de la segunda línea en file1.txt.

sed -i '2i\This is inserted text.' file1.txt

La -iopción es para edición en el lugar y la 2i\bandera inserta texto antes de la segunda línea.

Nota: Sin -i, la inserción solo se produce en la salida y no modifica el contenido del archivo. Para que el cambio sea persistente, debe usar la -iopción con el sedcomando.

Producción:

OutputLinux is a family of free and open-source operating systems based on the Linux kernel.
This is inserted text.
Operating systems based on Linux are known as Linux distributions or distros.
Example includes Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Linux, and many others.

Reemplazar la enésima ocurrencia de un patrón en una línea

Use los indicadores /1o /2para reemplazar la primera y la segunda aparición de un patrón en una línea. El siguiente comando reemplaza la segunda aparición de la palabra “Linux” por “Unix” en una línea.

sed 's/Linux/Unix/2' file1.txt

Producción:

OutputLinux is a family of free and open-source operating systems based on the Unix kernel.
This is inserted text.
Operating systems based on Linux are known as Unix distributions or distros.
Examples includes Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Linux, and many others.

Añadir cadena después de una línea

El siguiente comando añade “Este es el texto añadido” después de la tercera línea file1.txt. Esta -iopción garantiza que los cambios se guarden y 3a\añade el texto después de la tercera línea especificada.

sed -i '3a\This is appended text.' file1.txt

Producción:

OutputLinux is a family of free and open-source operating systems based on the Linux kernel.
This is inserted text.
Operating systems based on Linux are known as Linux distributions or distros.
This is appended text.
Example includes Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Linux, and many others.

Reemplazar una cadena al principio de una línea

La ^<pattern>bandera se usa para coincidir con un patrón específico al inicio de una línea. El siguiente comando reemplaza «Linux» por «Unix» solo si «Linux» aparece al inicio de una línea.

sed 's/^Linux/Unix/' file1.txt

Producción:

OutputUnix is a family of free and open-source operating systems based on the Linux kernel.
This is inserted text.
Operating systems based on Linux are known as Linux distributions or distros.
This is appended text.
Example includes Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Linux, and many others.

Reemplazar cadena al final de una línea

El siguiente comando reemplaza “distros.” por “distributions” solo si aparece al final de una línea. La <pattern>$bandera se usa para hacer coincidir un patrón específico con el final de una línea.

sed 's/distros.$/distributions/' file1.txt

Producción:

OutputLinux is a family of free and open-source operating systems based on the Linux kernel.
This is inserted text.
Operating systems based on Linux are known as Linux distributions or distributions.
This is appended text.
Example includes Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Linux, and many others.

Reemplazo sin distinción entre mayúsculas y minúsculas

El siguiente comando reemplaza “linux” por “Unix” sin distinguir entre mayúsculas y minúsculas. La Iopción hace que la coincidencia no distinga entre mayúsculas y minúsculas.

sed 's/linux/Unix/I' file1.txt

Extraer líneas entre patrones

El siguiente comando imprime todas las líneas entre “insertado” y “agregado”, inclusive.

sed -n '/inserted/,/appended/p' file1.txt
  1. ,:Operador de rango para hacer coincidir líneas entre dos patrones.
  2. p:Imprime líneas coincidentes.

Y la -nopción de suprimir la impresión automática de líneas.

Producción:

OutputThis is inserted text.
Operating systems based on Linux are known as Linux distributions or distros.
This is appended text.

Procesar varios archivos

El siguiente comando reemplaza “Linux” con “Unix” en ambos file1.txty file2.txtsobrescribe el archivo.

sed -i 's/Linux/Unix/' file1.txt file2.txt

Formato y numeración de líneas no vacías

El siguiente comando agrega números de línea a líneas no vacías en file1.txt.

sed '/./=' file1.txt | sed 'N;s/\n/ /'
  1. /./=: Coincide con líneas que no están vacías y las numera.
  2. N:Agrega la siguiente línea al espacio del patrón.
  3. s/\n/ /: Reemplaza el carácter de nueva línea con un espacio.

Producción:

Output1 Linux is a family of free and open-source operating systems based on the Linux kernel.
2 This is inserted text.
3 Operating systems based on Linux are known as Linux distributions or distros.
4 This is appended text.
5 Example includes Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Linux, and many others.

Reemplazar cadena en un número de línea específico

Puede restringir el sedcomando para que reemplace la cadena en una línea específica. El siguiente comando reemplaza la cadena “distros” por “distributions” solo en la tercera línea.

sed '3 s/distros/distributions/' file1.txt

Producción:

Linux is a family of free and open-source operating systems based on the Linux kernel.
This is inserted text.
Operating systems based on Linux are known as Linux distributions or distributions.
This is appended text.
Example includes Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Linux, and many others.

Reemplazo de cadena en un rango de líneas

También puede especificar un rango de números de línea para el sedcomando que reemplaza una cadena. El siguiente comando reemplaza solo las primeras apariciones de “Linux” por “Unix” entre las líneas 1 y 3.

sed '1,3 s/Linux/Unix/' file1.txt

Producción:

OutputUnix is a family of free and open-source operating systems based on the Linux kernel.
This is inserted text.
Operating systems based on Unix are known as Linux distributions or distros.
This is appended text.
Example includes Debian, Ubuntu, Fedora, CentOS, Gentoo, Arch Linux, and many others.

Consideraciones de rendimiento para archivos grandes

Procesar archivos grandes sedpuede consumir muchos recursos, especialmente al trabajar con numerosas operaciones o conjuntos de datos muy grandes. Aquí tienes algunos consejos para optimizar el rendimiento y garantizar un uso eficiente del sedcomando:

1. Usar -npara minimizar la salida innecesaria : Esta -nopción suprime la impresión automática de cada línea y garantiza que solo se muestre la salida deseada. Esto reduce la sobrecarga al trabajar con archivos grandes.

Ejemplo:

sed -n '/pattern/p' largefile.txt

2. Simplifique los scripts : minimice el número de operaciones en un solo comando. Por ejemplo, en lugar de ejecutar varios sedcomandos secuencialmente, combínelos en un solo script para reducir la lectura de archivos.

Ejemplo:

sed -e 's/foo/bar/' -e '/pattern/d' largefile.txt

3. Entrada de flujo con tuberías : al procesar datos de otros comandos o flujos, utilice tuberías para evitar la creación de archivos intermedios y reducir la E/S de disco.

Ejemplo:

cat largefile.txt | sed 's/foo/bar/' > output.txt

4. Evite la edición in situ en archivos grandes : en lugar de modificar directamente archivos grandes, escriba la salida en un nuevo archivo y reemplace el original después de verificar la corrección.

Ejemplo:

sed 's/old/new/' largefile.txt > temp.txt && mv temp.txt largefile.txt

5. Alternativas de referencia : para archivos muy grandes, considere usar herramientas como awk, perl, o grep, que pueden ofrecer un mejor rendimiento para ciertas tareas.

Ejemplo:

awk '{gsub(/old/, "new"); print}' largefile.txt > output.txt

Puede consultar estos tutoriales sobre el comando AWK en Linux y Cómo usar el lenguaje AWK para manipular texto en Linux para obtener más información sobre el uso de awkcomandos en Linux.

Integración con scripts de Shell

El sedcomando se usa comúnmente en scripts de shell para automatizar tareas repetitivas de manipulación de texto. A continuación, un ejemplo:

#!/bin/bash
# Replace all occurrences of "foo" with "bar" in input.txt and save the result
sed 's/foo/bar/g' input.txt > output.txt

Este script procesa input.txty escribe la salida modificada en output.txt.

sedvs otras alternativas

Si bien sedes una herramienta eficaz y ligera para el procesamiento básico de texto, alternativas modernas como awky perlofrecen funciones adicionales, lo que las hace más adecuadas para tareas específicas. A continuación, se detallan las diferencias clave y cuándo usar cada una:

Cuándo utilizarlosed

  • Sustituciones o eliminaciones de texto rápidas y sencillas.
  • Transformaciones basadas en líneas en archivos.
  • Tareas que requieren una sobrecarga de scripting mínima.

Cuándo utilizarloawk

  • Manejo de datos estructurados como archivos CSV o TSV.
  • Realizar cálculos aritméticos junto con el procesamiento de texto.
  • Generar informes formateados a partir de datos de entrada.

Ejemplo:

awk -F, '{print $1, $3}' data.csv

Esto extrae e imprime el primer y tercer campo de un archivo CSV.

Cuándo utilizarloperl

  • Manipulaciones de texto complejas que involucran expresiones regulares avanzadas.
  • Combinando el procesamiento de texto con lógica o condiciones.
  • Cómo escribir scripts compactos pero potentes.

Ejemplo:

perl -pe 's/(error)/WARNING: $1/' logfile.txt

Esto agrega un prefijo “ADVERTENCIA:” a las líneas que contienen la palabra “error”.

Conclusión

Dominar el sedcomando mejora tu capacidad para manipular y procesar texto eficientemente en Linux. Sus potentes funciones y su integración fluida con scripts lo convierten en una herramienta valiosa para tareas de manipulación de texto.

Próximos pasos

Tras dominar los conceptos básicos de sed, podrá aprender técnicas y casos de uso más avanzados. Puede utilizar la siguiente serie de tutoriales sobre sedtemas relacionados que le ayudarán a profundizar su comprensión y mejorar sus habilidades de procesamiento de texto:

  • Conceptos básicos del uso del sededitor de secuencias para manipular texto en Linux
  • Intermedio sed: Manipulación de flujos de texto en un entorno Linux

Estos tutoriales abarcan diversos temas, desde sedoperaciones básicas hasta técnicas más complejas de manipulación de texto. Son un recurso valioso para quienes desean dominar el procesamiento de texto en la línea de comandos.

Preguntas frecuentes

¿Qué es sedun comando en Linux?

El sedcomando “Editor de Flujo” en Linux es una potente herramienta de procesamiento de texto que permite realizar transformaciones básicas de texto en un flujo de entrada (un archivo o la entrada de una canalización). Permite buscar, reemplazar, eliminar e insertar texto, lo que lo hace muy útil para automatizar tareas de manipulación de texto.

¿Cuando utilizarlo sed?

Puede utilizarlo seden los siguientes escenarios:

  • Reemplazo de texto : reemplace palabras, frases o patrones en archivos o transmisiones.
  • Eliminación de texto : elimina líneas o patrones específicos.
  • Edición en el lugar : modifique archivos directamente sin necesidad de abrir un editor de texto.
  • Procesamiento por lotes : realice la misma operación en varios archivos mediante scripts.
  • Inserción/extracción de texto : inserte o extraiga texto específico en archivos estructurados, como archivos de configuración o registros.

¿Cómo utilizarlo sedcorrectamente?

Para utilizarlo sedeficazmente, siga estos pasos:

sed [options] 'command' file
  • command:La sedoperación (por ejemplo, spara sustituir, dpara eliminar).
  • file:El archivo de destino a procesar.

Prueba antes de aplicar en el lugar: primero, ejecute el comando sin la -iopción ‘ para ver el resultado antes de modificar los archivos directamente.

Utilice expresiones regulares: aproveche el soporte de sed para expresiones regulares para hacer coincidir y manipular patrones complejos.

Encadenar varios comandos: utilice ;o -epara ejecutar varios sedcomandos en una sola operación.

¿Cómo usar sed para reemplazar texto?

Para reemplazar texto, utilice el scomando substituir con esta sintaxis:

sed 's/old_text/new_text/' file

Ejemplos:

  • Reemplace la primera aparición de “foo” con “bar” en cada línea:
sed 's/foo/bar/' file.txt
  • Reemplace todas las ocurrencias de “foo” con “bar” globalmente:
sed 's/foo/bar/g' file.txt
  • Reemplazo en el lugar (modifica el archivo directamente):
sed -i 's/foo/bar/g' file.txt

¿Cómo ejecuto un sedcomando?

Puede ejecutar un sedcomando directamente desde la terminal utilizando esta sintaxis básica:

sed 'command' filename

Ejemplo:

Para imprimir líneas que contengan la palabra “error” y reemplazar “error” por “advertencia” en un archivo llamado log.txt:

sed 's/error/warning/' log.txt

¿Cuál es la diferencia entre los comandos grepy seden Linux?

Característicagrepsed
ObjetivoBuscar patrones en uno o más archivosEditar secuencias de texto
ProducciónImprime líneas que contienen el patrón.Imprime el texto editado
ComportamientoBuscar, filtrarBuscar, Reemplazar, Insertar, Eliminar
Usogrep pattern filesed 'command' file
Buscar y reemplazarSí (limitado)
Edición en el lugarNo
Expresiones regulares
Operaciones multilíneaNo
Filtrado de textoNo
Casos de uso comunesBúsqueda de registros, búsqueda de patrones en el textoEdición de archivos de configuración, reemplazo de texto en varios archivos

Ejemplo:

  • Usando greppara buscar “error” en log.txt:
grep 'error' log.txt
  • Usando sedpara reemplazar “error” con “advertencia” en log.txt:
sed 's/error/warning/g' log.txt

¿Cómo eliminar una línea vacía usando sed?

Para eliminar líneas vacías de un archivo, utilice el siguiente sedcomando:

sed '/^$/d' file.txt

Explicación:

  • ^$: Coincide con líneas vacías (líneas sin caracteres).
  • d:Elimina las líneas coincidentes.

Ejemplo:

Antes de ejecutar el comando, un archivo podría verse así:

line 1

line 2

line 3

Después de ejecutar el comando:

sed '/^$/d' file.txt

La salida será:

Outputline 1
line 2
line 3

Stephen Dove
Comments

April 27, 2025

Cómo crear una aplicación Web usando Flask en Python 3

Introducción

Flask es un marco web de Python pequeño y ligero que proporciona herramientas y funciones útiles que hacen que crear aplicaciones web en Python sea más fácil. Ofrece a los desarrolladores flexibilidad y un marco más accesible para los nuevos desarrolladores ya que puede crear una aplicación web rápidamente usando únicamente un archivo Python. Flask también es extensible y no fuerza una estructura de directorio concreta ni requiere código estándar complicado antes de iniciarse.

Como parte de este tutorial, usará el kit de herramientas Bootstrap para dar estilo a su aplicación, para que sea más atractiva visualmente. Bootstrap le ayudará a incorporar páginas web reactivas en su aplicación web para que también funcione bien en navegadores móviles sin tener que escribir su propio código HTML, CSS y JavaScript para conseguir estos objetivos. El kit de herramientas le permitirá centrarse en aprender cómo funciona Flask.

Flask utiliza el motor de plantillas Jinja para crear dinámicamente páginas HTML usando conceptos Python familiares como variables, bucles, listas, etcétera. Usará estas plantillas como parte de este proyecto.

En este tutorial, creará un pequeño blog web usando Flask y SQLite en Python 3. Los usuarios de la aplicación pueden ver todas las entradas del blog en su base de datos y hacer clic sobre el título de una entrada para ver su contenido, con la capacidad de añadir una nueva publicación a la base de datos y editar o eliminar una entrada existente.

Requisitos previos

Antes de comenzar con esta guía, necesitará lo siguiente:

  • Un entorno de programación Python 3, siguiendo el tutorial para su distribución en la serie Cómo instalar y configurar un entorno de programación local para Python 3 para su equipo local. En este tutorial, llamaremos al directorio de nuestro proyecto flask_blog.
  • Comprender los conceptos de Python 3, como tipos de datos, instrucciones condicionales, bucles for, funciones y otros conceptos similares. Si no está familiarizado con Python, consulte nuestra serie Cómo codificar en Python 3.

Paso 1: Instalar Flask

En este paso, activará su entorno Python e instalará Flask usando el instalador de paquetes pip.

Si aún no ha activado su entorno de programación, asegúrese de que está en el directorio de su proyecto (flask_blog) y utilice el siguiente comando para activar el entorno:

source env/bin/activate

Una vez activado su entorno de programación, su línea de comandos ahora tendrá un prefijo env que puede tener el siguiente aspecto:



Este prefijo es una indicación de que el entorno env está activo actualmente, y puede tener otro nombre dependiendo de cómo lo llamó durante la creación.

Nota: Puede utilizar Git, un sistema de control de versiones, para administrar y realizar un seguimiento de forma efectiva del proceso de desarrollo de su proyecto. Para aprender cómo usar Git, es posible que desee consultar nuestro artículo Introducción a la instalación de Git, uso y ramificaciones.

Si está usando Git, es una buena idea ignorar el recién creado directorio env en su archivo .gitignore para evitar realizar un seguimiento de los archivos no relacionados con el proyecto.

Ahora, instalará los paquetes Python y aislará el código de su proyecto de la instalación del sistema principal de Python. Hará esto usando pip y python.

Para instalar Flask, ejecute el siguiente comando:

pip install flask

Una vez que se complete la instalación, ejecute el siguiente comando para confirmar la instalación:

python -c "import flask; print(flask.__version__)"

Utiliza la interfaz de la línea de comandos python con la opción -c para ejecutar el código Python. A continuación, importa el paquete flask con import flask; luego imprime la versión de Flask, que encontrará a través de la variable flask._version_.

El resultado será un número de versión similar al siguiente:

Output1.1.2

Ha creado la carpeta del proyecto, un entorno virtual, e instalado Flask. Ahora está listo para configurar su aplicación básica.

Paso 2: Crear una aplicación básica

Ahora que tiene su entorno de programación configurado, comenzará a usar Flask. En este paso, creará una pequeña aplicación web dentro de un archivo Python, y la ejecutará para iniciar el servidor, que mostrará cierta información en el navegador.

En su directorio flask_blog, abra un archivo llamado hello.py para su edición; para ello utilice nano o su editor de texto favorito:

nano hello.py

Este archivo hello.py servirá como ejemplo mínimo de cómo gestionar solicitudes HTTP. Dentro, importará el objeto Flask, y creará una función que devuelva una respuesta HTTP. Escriba el siguiente código dentro de hello.py:

flask_blog/hello.py

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello():
    return 'Hello, World!'

En el bloque de código anterior, primero importa el objeto Flask desde el paquete flask. A continuación lo usará para crear su instancia de aplicación Flask con el nombre app. Pasa la variable especial __name__ que alberga el nombre del módulo Pyhthon actual. Se utiliza para indicar a la instancia dónde está ubicada. Necesitará hacerlo porque Flask configura algunas rutas en segundo plano.

Una vez que cree la instancia app, la utiliza para gestionar las solicitudes web entrantes y enviar respuestas al usuario. @app.route es un decorador que convierte una función Python regular en una función vista de Flask, que convierte el valor de devolución de la función en una respuesta HTTP que se mostrará mediante un cliente HTTP, como un navegador web. Pasa el valor '/' a @app.route() para indicar que esta función responderá a las solicitudes web para la URL /, que es la URL principal.

La función de vista hello() devuelve la cadena 'Hello, World!'​​ como respuesta.

Guarde y cierre el archivo.

Para ejecutar su aplicación web, primero indicará a Flask dónde encontrar la aplicación (el archivo hello.py en su caso) con la variable de entorno FLASK_APP:

export FLASK_APP=hello

A continuación, ejecútela en modo de desarrollo con la variable de entorno FLASK_ENV:

export FLASK_ENV=development

Por último, ejecute la aplicación usando el comando flask run:

flask run

Una vez que la aplicación se esté ejecutando, el resultado será algo similar a esto:

Output * Serving Flask app "hello" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 813-894-335

El resultado anterior tiene varias informaciones, como:

  • El nombre de la aplicación que está ejecutando.
  • El entorno en el cual se ejecuta la aplicación.
  • Debug mode: on significa que el depurador de Flask se está ejecutando. Esto es útil durante el desarrollo porque nos proporciona mensajes de error detallados cuando algo va mal, lo que hace que sea más fácil resolver los problemas.
  • La aplicación se ejecuta localmente sobre la URL http://127.0.0.1:5000/, 127.0.0.1 es la IP que representa el localhost de su equipo y :5000 es el número de puerto.

Abra un navegador y escriba la URL http://127.0.0.1:5000; recibirá la cadena Hello, World! como respuesta. Esto confirma que su aplicación se está ejecutando correctamente.

Advertencia Flask utiliza un servidor web sencillo para presentar nuestra aplicación en un entorno de desarrollo, lo que también significa que el depurador de Flask se ejecuta para hacer que sea más fácil ver los errores. Este servidor de desarrollo no debería usarse en una implementación de producción. Consulte la página Opciones de implementación en la documentación de Flask para obtener más información; también puede consultar este tutorial de Implementación Flask.

Ahora puede dejar el servidor de desarrollo en ejecución en el terminal y abrir otra ventana del terminal. Vaya a la carpeta del proyecto donde está hello.py, active el entorno virtual, establezca las variables del entorno FLASK_ENV y FLASK_APP y continúe a los siguientes pasos. (Estos comandos se han enumerado antes en este paso).

Nota: Cuando abra un nuevo terminal, es importante recordar activar el entorno virtual y configurar las variables de entorno FLASK_ENV y FLASK_APP.

Mientras un servidor de desarrollo de la aplicación de Flask esté ejecutándose, no es posible ejecutar otra aplicación Flask con el mismo comando flask run. Esto es porque flask run utiliza el número de puerto 5000 por defecto, y una vez utilizado, no está disponible para ejecutar otra aplicación, de forma que recibiría un error similar al siguiente:

OutputOSError: [Errno 98] Address already in use

Para resolver este problema, detenga el servidor que se está ejecutando actualmente mediante CTRL+C, luego ejecute flask run de nuevo, o si desea que ambos se ejecuten a la vez, puede pasar un número de puerto diferente al argumento -p; por ejemplo, para ejecutar otra aplicación en el puerto 5001 utilice el siguiente comando:

flask run -p 5001

Ahora tiene una aplicación web Flask pequeña. Ha ejecutado su aplicación y mostrado información en el navegador web. A continuación, utilizará los archivos HTML en su aplicación.

Paso 3: Usar plantillas HTML

Actualmente, su aplicación solo muestra un mensaje sencillo sin HTML. Las aplicaciones web utilizan principalmente HTML para mostrar información al visitante, de forma que ahora trabajará para incorporar archivos HTML en su aplicación, que puede ser mostrada en el navegador web.

Flask proporciona una función de ayuda render_template() que permite el uso del motor de plantillas Jinja. Esto hará que gestionar HTML sea mucho más fácil escribiendo su código HTML en archivos .html, además de utilizar la lógica en su código HTML. Usará estos archivos HTML, (plantillas), para crear todas las páginas de su aplicación, de igual forma que la página principal donde mostrará las entradas actuales del blog, la página de la entrada del blog, la página donde el usuario puede añadir una nueva entrada, etcétera.

En este paso, creará su aplicación Flask principal en un nuevo archivo.

Primero, en su directorio flask_blog, utilice nano o su editor de texto favorito para crear y editar su archivo app.py. Éste albergará todo el código que usará para crear la aplicación de blog:

nano app.py

En este nuevo archivo, importará el objeto Flask para crear una instancia de aplicación Flask, como hizo antes. También importará la función de ayuda render_template() que le permite representar archivos de plantilla HTML que existen en la carpeta templates que está a punto de crear. El archivo tendrá una función de vista única que será responsable de gestionar las solicitudes a la ruta principal /. Añada el siguiente contenido:

flask_blog/app.py

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

La función de vista index() devuelve el resultado de invocar render_template() con index.html como argumento; esto indica a render_template() que busque un archivo llamado index.html en la carpeta templates. La carpeta y el archivo no existen aún, y recibirá un error si ejecutase la aplicación en este momento. De todas formas la va a ejecutar, para que se familiarice con esta excepción que se encuentra comúnmente. Luego lo resolverá creando la carpeta y archivo necesarios.

Guarde el archivo y ciérrelo.

Detenga el servidor de desarrollo en su otro terminal que ejecuta la aplicación hello con CTRL+C.

Antes de ejecutar la aplicación, asegúrese de especificar correctamente el valor para la variable de entorno FLASK_APP, ya que ahora no está usando la aplicación hello.

export FLASK_APP=app
flask run

Al abrir la URL http://127.0.0.1:5000 en su navegador, se mostrará la página del depurador informándole de que no se ha encontrado la plantilla index.html. La línea de código principal en el código responsable de este error se resaltará. En este caso, es la línea return render_template('index.html').

Si hace clic en esta línea, el depurador revelará más código de forma que tenga más contexto para ayudarle a resolver el problema.

El Depurador de Flask

Para solucionar este error, cree un directorio llamado templates dentro de su directorio flask_blog. Dentro de él, abra un archivo llamado index.html para su edición:

mkdir templates
nano templates/index.html

A continuación, añada el siguiente código HTML dentro de index.html:

flask_blog/templates/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>FlaskBlog</title>
</head>
<body>
   <h1>Welcome to FlaskBlog</h1>
</body>
</html>

Guarde el archivo y utilice su navegador para navegar a http://127.0.0.1:500 de nuevo, o actualice la página. Esta vez, el navegador debería mostrar el texto Welcome to FlaskBlog en una etiqueta <h1>.

Además de la carpeta templates, las aplicaciones web de Flask también tienen normalmente una carpeta estática para albergar archivos, como los archivos CSS, archivos JavaScript, e imágenes que utiliza la aplicación.

Puede crear un archivo de hoja de estilo style.css para añadir CSS a su aplicación. Primero, cree un directorio llamado static dentro de su directorio flask_blog principal:

mkdir static

Luego cree otro directorio llamado css dentro del directorio static para albergar los archivos .css. Esto normalmente se hace para organizar archivos estáticos en carpetas dedicadas y, en consecuencia, los archivos JavaScript dentro de un directorio llamado js, las imágenes se ponen en un directorio llamado images (o img), etcétera. El siguiente comando creará el directorio css dentro del directorio static:

mkdir static/css

Luego abra un archivo style.css dentro del directorio css para su edición:

nano static/css/style.css

Añada la siguiente regla CSS a su archivo style.css:

flask_blog/static/css/style.css

h1 {
    border: 2px #eee solid;
    color: brown;
    text-align: center;
    padding: 10px;
}

El código CSS añadirá un borde, cambiará el color a marrón, centrará el texto y añadirá un pequeño relleno a las etiquetas <h1>.

Guarde y cierre el archivo.

A continuación, abra el archivo de plantilla index.html para su edición:

nano templates/index.html

Añadirá un enlace al archivo style.css dentro de la sección <head> del archivo de plantilla index.html:

flask_blog/templates/index.html

. . .
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="{{ url_for('static', filename= 'css/style.css') }}">
    <title>FlaskBlog</title>
</head>
. . .

Aquí utiliza la función de ayuda url_for() para generar la ubicación apropiada del archivo. El primer argumento especifica que está vinculando a un archivo estático, y el segundo argumento es la ruta del archivo dentro del directorio estático.

Guarde y cierre el archivo.

Tras actualizar la página de índice de su aplicación, observará que el texto Welcome to FlaskBlog es ahora marrón, está centrado y se encuadra dentro de un borde.

Puede utilizar el lenguaje CSS para dar estilo a la aplicación y hacer que sea más atractiva usando su propio diseño. Sin embargo, si no es un diseñador web, o no está familiarizado con CSS, puede usar el kit de herramientas Bootstrap, que ofrece componentes fáciles de usar para dar estilo a su aplicación. En este proyecto, usaremos Bootstrap.

Quizá haya imaginado que crear otra plantilla HTML significará repetir la mayoría del código HTML que ya escribió en la plantilla index.html. Puede evitar la repetición innecesaria de código con la ayuda de un archivo de plantilla base, desde el cual heredarán todos sus archivos HTML. Consulte Herencia de plantillas en Jinja para obtener más información.

Para crear una plantilla base, primero cree un archivo llamado base.html dentro de su directorio templates:

nano templates/base.html

Escriba el siguiente código en su plantilla base.html:

flask_blog/templates/base.html

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

    <title>{% block title %} {% endblock %}</title>
  </head>
  <body>
    <nav class="navbar navbar-expand-md navbar-light bg-light">
        <a class="navbar-brand" href="{{ url_for('index')}}">FlaskBlog</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
            <ul class="navbar-nav">
            <li class="nav-item active">
                <a class="nav-link" href="#">About</a>
            </li>
            </ul>
        </div>
    </nav>
    <div class="container">
        {% block content %} {% endblock %}
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  </body>
</html>

Guarde y cierre el archivo una vez que haya terminado de editarlo.

La mayor parte del código en el bloque anterior es HTML estándar y el código necesario para Bootstrap. Las etiquetas <meta> proporcionan información para el navegador web, la etiqueta <link> enlaza los archivos CSS de Boostrap y las etiquetas <script> son enlaces a código JavaScript que permite algunas funciones adicionales de Boostrap; consulte la documentación de Boostrap para obtener más información.

Sin embargo, las siguientes partes resaltadas son específicas para el motor de plantillas Jinja:

  • {% block title %} {% endblock %}: un bloque que sirve como marcador de posición para un título. Más adelante lo usará en otras plantillas para dar un título personalizado a cada página de su aplicación sin tener que reescribir toda la sección <head> cada vez.
  • {{ url_for('index')}: una invocación de función que devolverá la URL para la función de vista index(). Esto es diferente a la invocación anterior url_for() que utilizó para vincular a un archivo CSS estático, porque solo requiere un argumento, que es el nombre de la función de vista, y enlaza a la ruta asociada con la función en vez de con un archivo estático.
  • {% block content %} {% endblock %}: otro bloque que se sustituirá por contenido dependiendo de la plantilla secundaria (plantillas que heredan de base.html) que lo anulará.

Ahora que tiene una plantilla base, puede aprovecharla usando la herencia. Abra el archivo index.html:

nano templates/index.html

Sustituya su contenido con lo siguiente:

flask_blog/templates/index.html

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
{% endblock %}

En esta nueva versión de la plantilla index.html, utiliza la etiqueta {% extends %} para que herede de la plantilla base.html. Luego la extiende sustituyendo el bloque content en la plantilla base con lo que está dentro del bloque content en el bloque de código anterior.

Este bloque content contiene una etiqueta <h1> con el texto Welcome to FlaskBlog dentro de un bloque title, que a su vez sustituye el bloque title original en la plantilla base.html con el texto Welcome to FlaskBlog. De esta forma, puede evitar repetir el mismo texto dos veces, ya que funciona como título para la página y como encabezado que aparece bajo la barra de navegación heredada de la plantilla base.

La herencia de plantillas también le ofrece la capacidad de reutilizar el código HTML que tiene en otras plantillas (base.html en este caso), sin tener que repetirlo cada vez que sea necesario.

Guarde y cierre el archivo y actualice la página de índice en su navegador. Verá su página con una barra de navegación y un título con estilo.

Página de índice con Bootstrap

Utilizó plantillas HTML y archivos estáticos en Flask. También ha utilizado Bootstrap para comenzar a refinar el aspecto de su página y una plantilla base para evitar la repetición del código. En el siguiente paso, configurará una base de datos que almacenará los datos de su aplicación.

Paso 4: Configurar la base de datos

En este paso, configurará una base de datos para almacenar los datos, es decir, las entradas del blog para su aplicación. También completará la base de datos con algunas entradas de ejemplo.

Usará un archivo de base de datos SQLite para guardar sus datos porque el módulo sqlite3, que utilizaremos para interactuar con la base de datos, está disponible en la biblioteca Python. Para obtener más información sobre SQLite, consulte este tutorial.

Primero, ya que los datos de SQLite se guardan en tablas y columnas, y ya que sus datos principalmente son entradas de blog, primero deberá crear una tabla llamada posts con las columnas necesarias. Creará un archivo .sql que contiene comandos SQL para crear la tabla posts con algunas columnas. A continuación usará este archivo para crear la base de datos.

Abra un archivo llamado schema.sql dentro de su directorio flask_blog:

nano schema.sql

Escriba los siguientes comandos SQL dentro de este archivo:

flask_blog/schema.sql

DROP TABLE IF EXISTS posts;

CREATE TABLE posts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    title TEXT NOT NULL,
    content TEXT NOT NULL
);

Guarde y cierre el archivo.

El primer comando SQL es DROP TABLE IF EXISTS posts;, esto elimina cualquier tabla que exista llamada posts para que no tenga un comportamiento confuso. Observe que esto eliminará todo el contenido que tenga en la base de datos siempre que utilice estos comandos SQL, de forma que asegúrese de no escribir ningún contenido importante en la aplicación web hasta que haya terminado con este tutorial y experimente con el resultado final. A continuación, CREATE TABLE posts se utiliza para crear la tabla posts con las siguientes columnas:

  • id: es un entero que representa una clave primaria. La base de datos asignará un valor único para cada entrada (es decir, una entrada blog).
  • created: la hora en que se creó la entrada de blog. NOT NULL significa que esta columna no debería estar vacía y el valor DEFAULT es el valor CURRENT_TIMESTAMP, que es la hora en la cual la entrada se añadió a la base de datos. Igual que el id, no necesita especificar un valor para esta columna, y que se completará automáticamente.
  • title: el título de la entrada.
  • content: el contenido de la entrada.

Ahora que tiene un esquema SQL en el archivo schema.sql, lo usará para crear la base de datos usando un archivo Python que generará un archivo base de datos .db de SQLite. Abra un archivo llamado init_db.py dentro del directorio flask_blog usando su editor preferido:

nano init_db.py

Y luego añada el siguiente código.

flask_blog/init_db.py

import sqlite3

connection = sqlite3.connect('database.db')


with open('schema.sql') as f:
    connection.executescript(f.read())

cur = connection.cursor()

cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
            ('First Post', 'Content for the first post')
            )

cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
            ('Second Post', 'Content for the second post')
            )

connection.commit()
connection.close()

Primero importe el módulo sqlite3 y luego abra una conexión con el archivo de la base de datos llamada database.db, que se creará una vez que ejecute el archivo Python. Luego utilice la función open() para abrir el archivo schema.sql. A continuación, ejecute su contenido utilizando el método executescript() que ejecuta múltiples instrucciones SQL a la vez, lo que creará la tabla posts. Crea un objeto Cursor que le permite usar su método execute() para ejecutar dos instrucciones SQL INSERT para permitir dos entradas de blog en su tabla posts. Finalmente, confirma los cambios y cierra la conexión.

Guarde y cierre el archivo y a continuación ejecútelo en el terminal usando el comando python:

python init_db.py

Una vez que el archivo termine su ejecución, aparecerá un nuevo archivo llamado database.db en su directorio flask_blog. Esto significa que la configurado correctamente su base de datos.

En el siguiente paso, recuperará las entradas que insertó en su base de datos y las mostrará en la página de inicio de su aplicación.

Paso 5: Mostrar todas las entradas

Ahora que ha configurado su base de datos, puede modificar la función de vista index() para mostrar todas las entradas que tiene en su base de datos.

Abra el archivo app.py para realizar las siguientes modificaciones:

nano app.py

Para su primera modificación, importará el módulo sqlite3 en la parte superior del archivo:

flask_blog/app.py

import sqlite3
from flask import Flask, render_template

. . .

A continuación, creará una función que cree una conexión de base de datos y la devolverá. Añádala directamente tras las importaciones:

flask_blog/app.py

. . .
from flask import Flask, render_template

def get_db_connection():
    conn = sqlite3.connect('database.db')
    conn.row_factory = sqlite3.Row
    return conn

. . .

Esta función get_db_connection() abra una conexión con el archivo de base de datos database.db, y luego establece el atributo row_factory a sqlite3. Row para poder tener acceso basado en nombre a las columnas. Esto significa que la conexión con la base de datos devolverá filas que se comportan como diccionarios Python regulares. Por último, la función devuelve el objeto de conexión conn que usará para acceder a la base de datos.

Tras definir la función get_db_connection, modifique la función index() para que tenga el siguiente aspecto:

flask_blog/app.py

. . .

@app.route('/')
def index():
    conn = get_db_connection()
    posts = conn.execute('SELECT * FROM posts').fetchall()
    conn.close()
    return render_template('index.html', posts=posts)

En esta nueva versión de la función index(), primero abre una conexión de base de datos usando la función get_db_connection() que definió antes. A continuación, ejecuta una consulta SQL para seleccionar todas las entradas de la tabla post. Implementa el método fetchall() para recuperar todas las filas del resultado de la consulta. Esto devolverá una lista de las entradas que insertó en la base de datos en el paso anterior.

Cierra la conexión con la base de datos usando el método close() y devuelve el resultado de representar la plantilla index.html. También pasará el objeto posts como argumento, que contiene los resultados que obtuvo de la base de datos; esto le permitirá acceder a las entradas del blog en la plantilla index.html.

Con estas modificaciones implementadas, guarde y cierre el archivo app.py.

Ahora que ha pasado las entradas que recuperó de la base de datos a la plantilla index.html, puede usar un bucle for para mostrar cada entrada en su página de índice.

Abra el archivo index.html:

nano templates/index.html

A continuación, modifíquelo como sigue:

flask_blog/templates/index.html

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
    {% for post in posts %}
        <a href="#">
            <h2>{{ post['title'] }}</h2>
        </a>
        <span class="badge badge-primary">{{ post['created'] }}</span>
        <hr>
    {% endfor %}
{% endblock %}

Aquí, la sintaxis {% for post in posts %} es un bucle for Jinja, que es similar a un bucle for Python, excepto que tiene que ser cerrado posteriormente con la sintaxis {% endfor %}. Utiliza esta sintaxis para realizar un bucle sobre cada elemento en la lista posts que se pasó por la función index() en la línea return render_template('index.html', posts=posts). Dentro de este bucle for, muestra el título de la entrada en un encabezado <h2> dentro de una etiqueta <a> (más tarde utilizará esta etiqueta para vincular con cada entrada individualmente).

Muestra el título utilizando un delimitador variable literal ({{ ... }}). Recuerde que post será un objeto similar a un diccionario, para que pueda acceder al título de la entrada con post['title']​​​. También muestra la fecha de creación de la entrada usando el mismo método.

Una vez que haya terminado de editar el archivo, guarde y ciérrelo. Luego navegue a la página de índice en su navegador. Verá las dos entradas que añadió a la base de datos en su página.

Página de índice con entradas mostradas

Ahora que ha modificado la función de vista index() para mostrar todas las entradas que tiene en la base de datos en la página de inicio de su aplicación, pasará a mostrar cada entrada en una única página y permitirá que los usuarios vinculen a cada entrada individual.

Paso 6: Mostrar una entrada única

En este paso, creará una nueva ruta Flask con una función de vista y una nueva plantilla HTML para mostrar una entrada de blog individual por su ID.

Al final de este paso, la URL http://127.0.0.1:5000/1 será una página que muestra la primera entrada (porque tiene el ID 1). La URL http://127.0.0.1:5000/ID mostrará la entrada con el número ID asociado si existe.

Abra app.py para su edición:

nano app.py

Ya que necesitará obtener una entrada de blog por su ID de la base de datos en múltiples ubicación más adelante en este proyecto, creará una función independiente llamada get_post(). Puede invocarla pasándole un ID y recibiendo la entrada de blog asociada con el ID proporcionado, o hacer que Flask responsa con un mensaje 404 Not Found si la entrada de blog no existe.

Para responder con una página 404, deberá importar la función abort() desde la biblioteca Wekzeug, que se instaló junto con Flask en la parte superior del archivo:

flask_blog/app.py

import sqlite3
from flask import Flask, render_template
from werkzeug.exceptions import abort

. . .

A continuación, añada la función get_post() justo tras la función get_db_connection() que creó en el paso anterior:

flask_blog/app.py

. . .

def get_db_connection():
    conn = sqlite3.connect('database.db')
    conn.row_factory = sqlite3.Row
    return conn


def get_post(post_id):
    conn = get_db_connection()
    post = conn.execute('SELECT * FROM posts WHERE id = ?',
                        (post_id,)).fetchone()
    conn.close()
    if post is None:
        abort(404)
    return post

. . .

Esta nueva función tiene el argumento post_id que determina qué entrada de blog recuperar.

Dentro de la función, utiliza la función get_db_connection() para abrir una conexión de base de datos y ejecutar una consulta SQL para obtener la entada de blog asociada con el valor post_id dado. Añade el método fetchone() para obtener el resultado y almacenarlo en la variable post. Luego, cierre la conexión. Si la variable post tiene el valor None, lo que significa que no se ha encontrado ningún resultado en la base de datos, utiliza la función abort() que importó anteriormente para responder con un código de error 404 y la función terminará la ejecución. Si, sin embargo, se encuentra una entrada, devuelve el valor de la variable post.

A continuación, añada la siguiente función de vista al final del archivo app.py:

flask_blog/app.py

. . .

@app.route('/<int:post_id>')
def post(post_id):
    post = get_post(post_id)
    return render_template('post.html', post=post)

En esta nueva función de vista, añade una regla de variable <int:post_id> para especificar que la parte tras la barra (/) es un entero positivo (marcado con el conversor int) que necesita para acceder en su función de vista. Flask reconoce esto y pasa su valor al argumento de palabra clave post_id de su función de vista post(). A continuación, utiliza la función get_post() para obtener la entrada de blog asociada con el ID especificado y almacenar el resultado en la variable post, que pasa por una plantilla post.html que pronto creará.

Guarde el archivo app.py y abra un nuevo archivo de plantilla post.html para su edición:

nano templates/post.html

Escriba el siguiente código en este nuevo archivo post.html. Esto será similar al archivo index.html, excepto que solo mostrará una única entrada además de mostrar el contenido de la entrada:

flask_blog/templates/post.html

{% extends 'base.html' %}

{% block content %}
    <h2>{% block title %} {{ post['title'] }} {% endblock %}</h2>
    <span class="badge badge-primary">{{ post['created'] }}</span>
    <p>{{ post['content'] }}</p>
{% endblock %}

Añade el bloque title que definió en la plantilla base.html para hacer que el título de la página refleje el título de la entrada que se muestra en un encabezado <h2> al mismo tiempo.

Guarde y cierre el archivo.

Ahora puede navegar a la siguiente URL para ver las dos entradas que tiene en su base de datos, junto con una página que indica al usuario que no se encontró la entrada del blog solicitada (ya que no hay una entrada con el número de ID 3 hasta ahora):

http://127.0.0.1:5000/1
http://127.0.0.1:5000/2
http://127.0.0.1:5000/3

Volviendo a la página de índice, creará cada enlace al título de la entrada a su página respectiva. Hará esto usando la función url_for(). Primero, abra la plantilla index.html para su edición:

nano templates/index.html

A continuación cambie el valor del atributo href de # a {{ url_for('post', post_id=post['id']) }} de forma que el bucle for tendrá el siguiente aspecto:

flask_blog/templates/index.html

{% for post in posts %}
    <a href="{{ url_for('post', post_id=post['id']) }}">
        <h2>{{ post['title'] }}</h2>
    </a>
    <span class="badge badge-primary">{{ post['created'] }}</span>
    <hr>
{% endfor %}

Aquí, pasa 'post' a la función url_for() como primer argumento. Este es el nombre de la función de vista post() y ya que acepta un argumento post_id, le da el valor post['id']. La función url_for() devolverá la URL adecuada para cada entrada según su ID.

Guarde y cierre el archivo.

Los enlaces en la página de índice ahora funcionarán como se espera. Con esto, ha terminado de crear la parte de la aplicación responsable de mostrar las entradas de blog en su base de datos. A continuación, añadirá la capacidad de crear, editar y eliminar entradas de blog en su aplicación.

Paso 7: Modificar entradas

Ahora que ha terminado de mostrar las entradas de blog que están presentes en la base de datos en la aplicación web, deberá permitir a los usuarios de su aplicación escribir nuevas entradas de blog y añadirlas a la base de datos, editar las existentes y eliminar las entradas de blog innecesarias.

Crear una nueva entrada

Hasta este momento, tiene una aplicación que muestra las entradas en su base de datos pero no ofrece ninguna forma de añadir una nueva entrada a menos que conecte directamente con la base de datos SQLite y añada una manualmente. En esta sección, creará una página sobre la cual podrá crear una entrada proporcionando su título y contenido.

Abra el archivo app.py para su edición:

nano app.py

Primero, importará lo siguiente desde el marco Flask:

  • El objeto global request para acceder a los datos de solicitud entrantes que se enviarán a través de un formato HTML.
  • La función url_for() para generar URLs.
  • La función flash() para mostrar un mensaje cuando se procesa una solicitud.
  • La función redirect() para redirigir al cliente a una ubicación diferente.

Añada las importaciones a su archivo de la siguiente forma:

flask_blog/app.py

import sqlite3
from flask import Flask, render_template, request, url_for, flash, redirect
from werkzeug.exceptions import abort

. . .

La función flash() almacena los mensajes mostrados en la sesión del navegador del cliente, lo que requiere configurar una clave secreta. Esta clave secreta se utiliza para proteger las sesiones, lo que permite a Flask recordar la información de una solicitud a la otra, como pasar desde la nueva página de entrada a la página de índice. El usuario puede acceder a la información almacenada en la sesión, pero no puede modificarla a menos que tenga la clave secreta, de forma que nunca permita que nadie acceda a la clave secreta. Consulte la documentación de Flask para sesiones para obtener más información.

Para establecer una_ clave_ secreta, añadirá una configuración SECRET_KEY a su aplicación a través del objeto app.config. Añádala directamente siguiendo la definición app antes de definir la función de vista index():

flask_blog/app.py

. . .
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your secret key'


@app.route('/')
def index():
    conn = get_db_connection()
    posts = conn.execute('SELECT * FROM posts').fetchall()
    conn.close()
    return render_template('index.html', posts=posts)

. . .

Recuerde que la clave secreta debería ser una cadena aleatoria larga.

Tras establecer una clave secreta, creará una función de vista que mostrará una plantilla que muestra un formulario que puede completar para crear una nueva entrada de blog. Añada esta nueva función en la parte inferior del archivo:

flask_blog/app.py

. . .

@app.route('/create', methods=('GET', 'POST'))
def create():
    return render_template('create.html')

Esto crea una ruta /create que acepta las solicitudes GET y POST. Las solicitudes GET se aceptan por defecto. También acepta las solicitudes POST que se envía el navegador cuando se envían formularios. Pasará un tuple con los tipos de solicitudes aceptadas al argumento methods del decorador @app.route().

Guarde y cierre el archivo.

Para crear la plantilla, abra un archivo llamado create.html dentro de su carpeta templates:

nano templates/create.html

Añada el siguiente código dentro de este nuevo archivo:

flask_blog/templates/create.html

{% extends 'base.html' %}

{% block content %}
<h1>{% block title %} Create a New Post {% endblock %}</h1>

<form method="post">
    <div class="form-group">
        <label for="title">Title</label>
        <input type="text" name="title"
               placeholder="Post title" class="form-control"
               value="{{ request.form['title'] }}"></input>
    </div>

    <div class="form-group">
        <label for="content">Content</label>
        <textarea name="content" placeholder="Post content"
                  class="form-control">{{ request.form['content'] }}</textarea>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">Submit</button>
    </div>
</form>
{% endblock %}

La mayor parte de este código es HTML estándar. Mostrará un cuadro de entrada para el título de la entrada, un área de texto para el contenido de la entrada y un botón para enviar el formulario.

El valor de la entrada del título de la entrada es {{ request.form['title'] }},​​​ y el área de texto tiene el valor {{ request.form['content'] }}. Esto se hace para que los datos que introduce no se pierdan si algo sale mal. Por ejemplo, si escribe una entrada larga y olvida darle un título, se mostrará un mensaje que le informa de que el título es necesario. Esto sucederá sin perder la entrada que escribió ya que se almacenará en el objeto global request al que tiene acceso en sus plantillas.

Ahora, con el servidor de desarrollo en ejecución, utilice su navegador para navegar a la ruta /create:

http://127.0.0.1:5000/create

Verá una página Create a New Post con un cuadro para un título y contenido:

Crear una nueva página de entrada

Este formulario envía una solicitud POST a su función de vista create(). Sin embargo, aún no hay ningún código para gestionar una solicitud POST en la función, de forma que no sucederá nada tras completar el formulario y enviarlo.

Gestionará la solicitud POST entrante cuando se envía un formulario. Lo hará dentro de la función de vista create(). Puede gestionar por separado la solicitud POST comprobando el valor de request.method. Cuando su valor se establece a 'POST', significa que la solicitud es una solicitud POST. Luego extraerá los datos enviados, los validará y los insertará en su base de datos.

Abra el archivo app.py para su edición:

nano app.py

Modifique la función de vista create() para que tenga exactamente el siguiente aspecto:

flask_blog/app.py

. . .

@app.route('/create', methods=('GET', 'POST'))
def create():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']

        if not title:
            flash('Title is required!')
        else:
            conn = get_db_connection()
            conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)',
                         (title, content))
            conn.commit()
            conn.close()
            return redirect(url_for('index'))

    return render_template('create.html')

En la instrucción if asegura que el código que le sigue solo se ejecuta cuando la solicitud es una solicitud POST a través de la comparativa request.method == 'POST'.

A continuación, extrae el título enviado y el contenido desde el objeto request.form que le proporciona acceso a los datos del formulario en la solicitud. Si no se proporciona el título, la condición if not title se cumplirá, mostrando un mensaje al usuario informándole de que el título es obligatorio. Si, por otro lado, se proporciona el título, abrirá una conexión con la función get_db_connection() e insertará el título y el contenido que recibió en la tabla posts.

Luego confirma los cambios en la base de datos y cierra la conexión. Tras añadir la entrada de blog a la base de datos, redirige al cliente a la página de índice usando la función redirect() pasándole la URL generada por la función url_for() con el valor 'index' como argumento.

Guarde y cierre el archivo.

Ahora, navegue a la ruta /create usando su navegador web:

http://127.0.0.1:5000/create

Complete el formulario con un título y algo de contenido. Una vez que envíe el formulario, verá la nueva entrada listada en la página de índice.

Por último, mostrará los mensajes generados y añadirá un enlace a la barra de navegación en la plantilla base.html para tener un acceso fácil a esta nueva página. Abra el archivo de plantilla:

nano templates/base.html

Edite el archivo añadiendo una nueva etiqueta <li> tras el enlace About dentro de la etiqueta <nav>. A continuación, añada un nuevo bucle for directamente sobre el bloque content para mostrar los mensajes generados bajo la barra de navegación. Estos mensajes están disponibles en la función especial get_flashed_messages() que Flask ofrece:

flask_blog/templates/base.html

<nav class="navbar navbar-expand-md navbar-light bg-light">
    <a class="navbar-brand" href="{{ url_for('index')}}">FlaskBlog</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
        <ul class="navbar-nav">
        <li class="nav-item">
            <a class="nav-link" href="#">About</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{{url_for('create')}}">New Post</a>
        </li>
        </ul>
    </div>
</nav>
<div class="container">
    {% for message in get_flashed_messages() %}
        <div class="alert alert-danger">{{ message }}</div>
    {% endfor %}
    {% block content %} {% endblock %}
</div>

Guarde y cierre el archivo. La barra de navegación ahora tendrá un elemento New Post que enlaza a la ruta /create.

Editar una entrada

Para que un blog esté actualizado, deberá poder editar sus entradas existentes. Esta sección le guiará en la creación de una nueva página en su aplicación para simplificar el proceso de editar una entrada.

Primero, añadirá una nueva ruta al archivo app.py. Su función de vista recibirá el ID de la entrada que debe ser editada, la URL estará en el formato /post_id/edit con la variable post_id siendo el ID de la entrada. Abra el archivo app.py para su edición:

nano app.py

A continuación, añada la siguiente función de vista edit() al final del archivo. Editar una entrada existente es similar a crear una nueva, de forma que esta función de vista será similar a la función de vista create():

flask_blog/app.py

. . .

@app.route('/<int:id>/edit', methods=('GET', 'POST'))
def edit(id):
    post = get_post(id)

    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']

        if not title:
            flash('Title is required!')
        else:
            conn = get_db_connection()
            conn.execute('UPDATE posts SET title = ?, content = ?'
                         ' WHERE id = ?',
                         (title, content, id))
            conn.commit()
            conn.close()
            return redirect(url_for('index'))

    return render_template('edit.html', post=post)

La entrada que edita viene determinada por la URL y Flask pasará el número de ID a la función edit() a través del argumento id. Añade este valor a la función get_post() para recuperar la entrada asociada con el ID proporcionado desde la base de datos. Los nuevos datos vendrán en una solicitud POST, que se gestiona dentro de la condición if request.method == 'POST'.

Igual que cuando creó una nueva entrada, primero extrae los datos del objeto request.form, luego muestra un mensaje si el título tiene un valor vacío, de lo contrario, abre una conexión con la base de datos. Luego actualiza la tabla posts estableciendo un nuevo título y nuevo contenido donde el ID de la entrada en la base de datos es igual al ID que estaba en la URL.

En el caso de una solicitud GET, representa una plantilla edit.html pasando la variable post que alberga el valor devuelto de la función get_post(). Usará esto para mostrar el título existente y el contenido en la página de edición.

Guarde y cierre el archivo, y cree una nueva plantilla edit.html:

nano templates/edit.html

Escriba el siguiente código dentro de este nuevo archivo:

flask_blog/templates/edit.html

{% extends 'base.html' %}

{% block content %}
<h1>{% block title %} Edit "{{ post['title'] }}" {% endblock %}</h1>

<form method="post">
    <div class="form-group">
        <label for="title">Title</label>
        <input type="text" name="title" placeholder="Post title"
               class="form-control"
               value="{{ request.form['title'] or post['title'] }}">
        </input>
    </div>

    <div class="form-group">
        <label for="content">Content</label>
        <textarea name="content" placeholder="Post content"
                  class="form-control">{{ request.form['content'] or post['content'] }}</textarea>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">Submit</button>
    </div>
</form>
<hr>
{% endblock %}

Guarde y cierre el archivo.

Este código sigue el mismo patrón excepto por la sintaxis {{ request.form['title'] or post['title'] }} y {{ request.form['content'] or post['content'] }}. Esto muestra los datos guardados en la solicitud si existe, de lo contrario, muestra los datos de la variable post que se pasó a la plantilla que contiene los datos actuales de la base de datos.

Ahora, navegue a la siguiente URL para editar la primera entrada:

http://127.0.0.1:5000/1/edit

Verá una página Edit “First Post”.

Editar la página de una entrada

Edite la entrada y envíe el formulario, luego asegúrese de que la entrada se ha actualizado.

Ahora necesita añadir un enlace que apunte a la página de edición para cada entrada en la página de índice. Abra el archivo de plantilla index.html:

nano templates/index.html

Edite el archivo para que tenga exactamente el siguiente aspecto:

flask_blog/templates/index.html

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
    {% for post in posts %}
        <a href="{{ url_for('post', post_id=post['id']) }}">
            <h2>{{ post['title'] }}</h2>
        </a>
        <span class="badge badge-primary">{{ post['created'] }}</span>
        <a href="{{ url_for('edit', id=post['id']) }}">
            <span class="badge badge-warning">Edit</span>
        </a>
        <hr>
    {% endfor %}
{% endblock %}

Guarde y cierre el archivo.

Aquí añade una etiqueta <a> para enlazar a la función de vista edit(), pasando el valor post['id'] al enlace para editar la página de cada entrada con el enlace Edit.

Eliminar una entrada

A veces, no es necesario que una entrada siga estando disponible públicamente, y por eso la funcionalidad de eliminar una entrada es crucial. En este paso, añadirá la funcionalidad de eliminación a su aplicación.

Primero, añadirá una nueva ruta /ID/delete que acepta solicitudes POST, de forma similar a la función de vista edit(). Su nueva función de vista delete() recibirá el ID de la entrada que será eliminada de la URL. Abra el archivo app.py:

nano app.py

Añada la siguiente función de vista en la parte inferior del archivo:

flask_blog/app.py

# ....

@app.route('/<int:id>/delete', methods=('POST',))
def delete(id):
    post = get_post(id)
    conn = get_db_connection()
    conn.execute('DELETE FROM posts WHERE id = ?', (id,))
    conn.commit()
    conn.close()
    flash('"{}" was successfully deleted!'.format(post['title']))
    return redirect(url_for('index'))

Esta función de vista solo acepta solicitudes POST. Esto significa que navegar a la ruta /ID/delete en su navegador devolverá un error porque los navegadores web van de forma predeterminada a las solicitudes GET.

Sin embargo, puede acceder a esta ruta mediante un formulario que envía una solicitud POST pasando el ID de la entrada que desea eliminar. La función recibirá el valor de ID y lo usará para obtener la entrada de la base de datos con la función get_post().

A continuación, abrirá una conexión de base de datos y ejecutará un comando SQL DELETE FROM para eliminar la entrada. Confirma el cambio en la base de datos y cierra la conexión mientras muestra un mensaje para informar al usuario de que la entrada se eliminó correctamente y redirigirlo a la página de índice.

Observe que no reproduce un archivo de plantilla. Esto es porque añadirá un botón Delete a la página de edición.

Abra el archivo de plantilla edit.html:

nano templates/edit.html

Luego, añada la siguiente etiqueta <form> tras la etiqueta <hr> y directamente antes de la línea {% endblock %}:

flask_blog/templates/edit.html

<hr>

<form action="{{ url_for('delete', id=post['id']) }}" method="POST">
    <input type="submit" value="Delete Post"
            class="btn btn-danger btn-sm"
            onclick="return confirm('Are you sure you want to delete this post?')">
</form>

{% endblock %}

Utiliza el método confirm() para mostrar un mensaje de confirmación antes de enviar la solicitud.

Ahora, navegue de nuevo a la página de edición de una entrada de blog e intente eliminarla:

http://127.0.0.1:5000/1/edit

Al final de este paso, el código fuente de su proyecto tendrá el aspecto del código en esta página.

Con esto, los usuarios de su aplicación pueden escribir nuevas entradas de blog y añadirlas a la base de datos, editar entradas y eliminar las entradas existentes.

Conclusión

Este tutorial ha introducido los conceptos esenciales del marco Flask Python. Ha aprendido a crear una pequeña aplicación web, ejecutarla en un servidor de desarrollo y a permitir al usuario proporcionar datos personalizados a través de parámetros URL y formularios web. También utilizó el motor de plantillas Jinja para reutilizar los archivos HTMP y usar lógica en ellos. Al final de este tutorial, tendrá un blog web completamente funcional que interactúa con una base de datos SQLite para crear, mostrar, editar y eliminar entradas de blog usando el lenguaje Python y las consultas SQL.

Puede seguir desarrollando esta aplicación añadiendo autenticación del usuario de forma que solo los usuarios registrados puedan crear o modificar entradas de blog. También puede añadir comentarios y etiquetas para cada entrada de blog, y añadir subidas de archivos para dar a los usuarios la capacidad de incluir imágenes en la entrada del blog. Consulte la documentación de Flask para obtener más información.

Flask cuenta con muchas extensiones Flask creadas por la comunidad. A continuación verá una lista de extensiones que quizá quiera considerar usar para que su proceso de desarrollo sea más fácil:

  • Flask-Login: gestiona la sesión del usuario y el inicio y cierre de sesión, además de recordar a los usuarios con sesión iniciada.
  • Flask-SQLAlchemy: simplifica el uso de Flask con SQLAlchemy, un kit de herramientas Python SQL y un Asignador relacional de objetos para interactuar con bases de datos SQL.
  • Flask-Mail: ayuda con la tarea de enviar mensajes de correo electrónico en su aplicación Flask.

Stephen Dove