Análisis y Organización de Historial de Comandos en Arch Linux con Python
Tags: python, linux, archlinux, zsh
Published: 2024-01-26 23:05:50

Como entusiasta y usuario de Arch Linux, siempre estoy buscando maneras de optimizar mi flujo de trabajo y entender mejor cómo interactúo con mi sistema. Uno de los aspectos fascinantes de trabajar con Linux es la gran cantidad de información que puedo obtener a través de la terminal, especialmente el historial de comandos.

Recientemente, he desarrollado un script en Python que me permite analizar y organizar mi historial de comandos de la consola `zsh`, enfocándome especialmente en los comandos relacionados con la instalación y desinstalación de paquetes. Aquí os explico cómo funciona mi script y cómo lo utilizo para mejorar mi experiencia en Arch Linux y tener un respaldo en caso de que quiera hacer un reset.

## El Propósito del Script

El principal objetivo de mi script es filtrar y organizar los comandos de instalación y desinstalación que he utilizado en mi terminal `zsh`. Al hacer esto, puedo tener una visión clara de qué paquetes he instalado o eliminado, lo cual es crucial para mantener un sistema limpio y eficiente.

## Funcionamiento del Script

El script utiliza Python, un lenguaje poderoso y versátil, para leer mi archivo de historial `.zsh_history`. Mediante el uso de listas y diccionarios en Python, clasifico los comandos basándome en si son de instalación o desinstalación.

Aquí hay una breve explicación técnica de cómo funciona:


1. **Lectura del Archivo de Historial**: El script comienza leyendo el archivo `~/.zsh_history`, que contiene todos mis comandos ejecutados en `zsh`.

2. **Filtrado de Comandos**: A continuación, el script filtra los comandos basándose en un conjunto predefinido de acciones de instaladores (`pacman`, `yay`, `flatpak`).

3. **Organización de los Comandos**: Los comandos se organizan en un diccionario, separando los comandos de instalación de los de desinstalación.


## Ejemplo de Uso y Salida

Después de ejecutar el script, obtengo una salida estructurada que me muestra qué paquetes he instalado y cuáles he desinstalado. Por ejemplo, si ejecuto un comando para instalar `firefox` y luego otro para desinstalarlo, mi script lo detectará y organizará adecuadamente.

Aquí es donde podría incluir una captura de pantalla del terminal mostrando la salida del script:


![Output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pyxioqyrz8upeywklbrf.png)



## Explicación del Código

El corazón del script es una función denominada `save_map`, que analiza cada línea del historial y decide si es un comando relevante. Si encuentra un comando de instalación o desinstalación, lo procesa y lo almacena en el diccionario `installation_commands_map`.

### Imports y configuraciones iniciales
```python
import os
import json

# Define la ruta al archivo de historial de zsh
history_path = os.path.expanduser('~/.zsh_history')

# Define los comandos de instalación y desinstalación para diferentes gestores de paquetes
INSTALLER_COMMANDS = [
    {"command":'pacman', "install_command":'-S', "uninstall_command":'-Rns'},
    {"command":'yay', "install_command":'-S', "uninstall_command":'-Rns'}, 
    {"command":'flatpak', "install_command":'install',"uninstall_command":'uninstall'}
    ]
```
### Tratado de datos
```python
try:
    # Inicializa un diccionario para mapear comandos de instalación y desinstalación
    installation_commands_map = {'uninstall':[]}
    for installer in INSTALLER_COMMANDS:
        name_com = installer['command']
        installation_commands_map[name_com] = []

    # Define una función para procesar y guardar los comandos relevantes del historial
    def save_map(command):
        for installer in INSTALLER_COMMANDS:
            name_com = installer['command']
            install_com = installer['install_command']
            uninstall_com = installer['uninstall_command']

            # Agrega comandos de instalación al mapa
            if name_com in split_command and install_com in split_command:
                filtered_command = [part for part in split_command if part != 'sudo']
                installation_commands_map[name_com].append(' '.join(filtered_command))

            # Agrega comandos de desinstalación al mapa y elimina el paquete de la lista de instalados
            if name_com in split_command and uninstall_com in split_command:
                filtered_command = [part for part in split_command if part != 'sudo']
                installation_commands_map['uninstall'].append(' '.join(filtered_command))
                package_to_uninstall = split_command[-1]
                installation_commands_map[name_com] = [
                    com for com in installation_commands_map[name_com]
                    if package_to_uninstall not in com
                ]
    # Abre y lee el archivo de historial línea por línea
    with open(history_path, 'r', encoding='utf-8', errors='ignore') as file:
        history = file.readlines()
        
        for command in history:
            striped = command.strip()
            if(striped != '' ):
                split_command_time = striped.split(';')[1]
                split_command = split_command_time.split(' ')
                if( len(split_command)>=3):
                    save_map(split_command)
    # Escribe el mapa de comandos procesados a un archivo JSON
    with open('history_commands_map.json', 'w', encoding='utf-8') as json_file:
        json.dump(installation_commands_map, json_file, ensure_ascii=False, indent=4)
```
### Manejo de excepciones
```python
except FileNotFoundError:
    print(f"File not found in: {history_path}")
except Exception as e:
    print(f"Error: {e}")
```

## Conclusiones y Mejoras

Este script es un ejemplo perfecto de cómo, un poco de conocimiento de `zsh` y `Python` puede combinarse para crear herramientas personalizadas que mejoren tu experiencia como usuario de cualquier sistema, en mi caso, de Arch.