Python - Lección 20: Python y Bash scripting

Python y Bash Scripting

Dado que en este foro somos personas de bien y nuestro sistema operativo es una distribución de Linux, nos encontraremos con que Bash es el lenguaje nativo del sistema y consiste en crear archivos o scripts con la capacidad de ejecutar comandos de Linux, permitiéndonos automatizar tareas del sistema sin tener que ingresar constantemente los mismos comandos una y otra vez.
Podemos combinar los comandos y los resultados de la ejecución de los mismos con elementos típicos de la programación como el uso de variables, operaciones matemáticas, estructuras condicionales y bucles, dandonos el poder de operar nuestro sistema de manera más sofisticada.

En este momento deben estar preguntándose “¿Y qué tiene que ver esto con la programación en Python?” y es que este lenguaje cuenta con una librería muy útil que nos permite ejecutar comandos en bash o incluso ejecutar scripts de bash utilizando comandos de Linux, aumentando aún más el poder del programador para lograr sus objetivos o extender sus capacidades.

Para este curso no vamos a aprender cómo programar en bash, pero sí veremos como Python puede ejecutar comandos o scripts y utilizar esos resultados en nuestro software.

Para poder ejecutar un comando de Linux en Python, primero debemos importar la librería os:

import os

os.system("sudo apt update && sudo apt upgrade")

Así de sencillo tenemos un script de Python que actualiza nuestro sistema operativo y sólo utilizamos dos líneas de código.

Utilizando el formato de las cadenas de texto, podemos modificar estas para crear, por ejemplo, archivos de texto que contengan valores específicos:

import os

resultado = 3 + 4

os.system(f"echo '{resultado}' > archivo.txt")

os.system("cat archivo.txt")

Ahora vamos a ver cómo guardar nuestra dirección IP en una variable de Python. Para ello, vamos a utilizar la librería subprocess:

  • Primero vamos a tener establecer el comando en bash que nos retornará nuestra dirección IP:
ip a | grep wlan0 | awk 'NR==2' | awk '{print $2}' | awk '{print $1}' FS='/'

Traducción:

ip a Muestra las interfaces de red
grep wlan 0 Filtra por la interfáz ‘wlan0’
awk ‘NR==2’ Filtra por la segunda fila, se puede reemplazar con ‘tail -n 1’
awk ‘{print $2}’ Imprime el segundo argumento de la fila
awk ‘{print $1}’ FS=‘/’ Imprime el primer elemento, tomando como delimitador el caracter especificado
cut -d ‘/’ -f 1 Corta el argumento en el caracter especificado y toma el elemento 1 de dicha división

Teniendo identificado el comando que necesitamos, en Python vamos a crear un programa que ejecuta el comando, almacena el resultado en una variable a través de una función y luego la imprime por pantalla:

# Importamos la librería:
import subprocess

# Ejecutamos el método check_output de dicha librería para ejecutar el comando y almacenar el output en la variable
mi_IP = subprocess.check_output ("ip a | grep wlan0 | awk 'NR==2' | awk '{print $2}' | awk '{print $1}' FS='/'", shell=True)

# Ahora podemos imprimir el resultado del comando:
print (f"Mi dirección IP es: {mi_IP}")

Vamos a elevar el nivel un poco más y vamos a crear un script en bash que nos permita identificar a todos los hosts activos en nuestra red. No se preocupen por entender bash en este momento, lo explicaré en el curso de ese lenguaje… cuando al fin pueda hacerlo jajajaja

  • Primero vamos a crear un script en bash:

hostScan.sh

#!/bin/bash

#Colours
greenColour="\e[0;32m\033[1m"
endColour="\033[0m\e[0m"
redColour="\e[0;31m\033[1m"
blueColour="\e[0;34m\033[1m"
yellowColour="\e[0;33m\033[1m"
purpleColour="\e[0;35m\033[1m"
turquoiseColour="\e[0;36m\033[1m"
grayColour="\e[0;37m\033[1m"
orangeColour="\e[0;208m\033[1m"

declare -a ports=( $(seq 1 65535) )

function ctrl_c(){
        echo -e "\n\n${redColour}[!] Saliendo...${endColour}\n"
        tput cnorm; exit 1
}

# Ctrl + C

trap ctrl_c SIGINT

function checkHost(){
        for i in $(seq 1 254); do
                timeout 1 bash -c "ping -c 1 $1.$i &>/dev/null" && echo -e "${greenColour}[+]${endColour}${grayColour} Host $1.${endColour}${turquoiseColour}$i${endColour}${grayColour} - ${endColour}${greenColour}ACTIVE${endColour}" &
        done
}

if [ $1 ]; then
        checkHost $1 &
else
        echo -e "${redColour}[!]${endColour}${grayColour} Uso:${endColour}${turquoiseColour} $0${endColour}${orangeColour} <Primeros tres octetos de IP, ej: 192.168.1>${endColour}\n"
fi

wait
  • Ahora vamos a ejecutar ese script de Bash con un script de Python:

pythonScript.py

# Importamos la librería:
import os

os.system("/home/[usuario]/hostScan.sh")

Al ejecutar el comando, nos devolverá un error avisándonos de que no tenemos permisos para ejecutar el script de Bash. Los permisos son un tema en Linux que requieren de bastante atención y que tiene su complejidad, por lo que voy a omitir las explicaciones en este momento.
Para resumirlo de manera sencilla, el script de Bash no se ejecutará porque no posee permisos de ejecución. Para solucionar esto, deberemos cambiarle los permisos de ejecución con el siguiente comando:

chmod +x hostScan.sh

-Una vez hecho esto, el script de Python ejecutará el script de Bash, el cual comenzará a listarnos todas las direcciones IP activas en nuestra red.
Esta es una herramienta muy simple pero a la vez muy útil que nos permitirá ver la cantidad de equipos conectados a nuestra red y la dirección IP que posee.

Al involucrar Bash, las cosas se complejizan mucho más y el tema se vuelve por demás extenso. Desafío a cualquiera que este haciendo el curso a que pruebe y experimente esta combinación y también, que comparta sus descubrimientos respondiendo a este tema.

<Tema anterior - Siguiente tema>

2 Me gusta

Yo prefiero emplear el siguiente comando porque permite obtener la salida y
los errores por separado además de el código de retorno (para conocer si el
comando se ejecuto con éxito).

command_info = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if command_info.returncode :
	print("exito")
3 Me gusta

Lo voy a agregar. Conozco muchos procedimientos para ese objetivo y varios incluso usan otras librerías. Me estaba debatiendo si valía la pena incluirlos a todos, ya que muchos cumplen el mismo propósito pero con diferentes funciones.
Me paso con la conexión a MySQL, para la cual existen muchas librerías pero tengo una de preferencia, la cual no me ha fallado nunca e incluso me deja hacer cosas que las otras no.

Muchas gracias por tu aporte.

Perdón por la demora. Acabo de añadir tu comentario como marcador para ocuparme de esto antes de seguir con más temas. Quería cerrar el capítulo antes de pulir lo que faltaba.