Parte 2 — Herramientas, toolchains y anatomía de comandos · ⏱️ Duración estimada: 75 min · Nivel: Fundamentos ✅ Clase construida.
Cuando escribes python y pulsas Enter, ocurre algo que casi nadie se para a pensar: el sistema no sabe dónde está Python. Tiene que buscarlo, y lo hace recorriendo en orden una lista de carpetas que alguien configuró antes. Esa lista es el PATH, y es una de las varias variables de entorno que el sistema entrega a cada programa que arranca. Esta clase trata de ese entorno invisible en el que corren tus comandos, y de por qué entenderlo explica de golpe una familia entera de problemas que de otro modo parecen magia negra: el command not found de una herramienta que acabas de instalar, la versión equivocada de un intérprete que aparece sin motivo, el script que funciona en tu terminal y falla en el servidor de integración continua.
El mecanismo importa porque es la frontera entre tu programa y el sistema que lo hospeda. Kernighan y Pike describen el entorno como parte del contexto que un proceso hereda al nacer, y esa palabra —heredar— es la clave que ordena todo lo demás. Shotts dedica en The Linux Command Line un capítulo entero a este asunto por la misma razón: quien no entiende cómo se propaga el entorno no puede diagnosticar por qué su configuración funciona en un sitio y no en otro. Al terminar deberías poder abrir una terminal ante un command not found y llegar a la causa en menos de un minuto, en lugar de reinstalar cosas al azar.
Al finalizar, podrás:
command not found razonando sobre el PATH y su orden de búsqueda.| # | Tema | Por qué importa |
|---|---|---|
| 1 | Variables de entorno | Configuración que reciben los programas |
| 2 | El PATH | Dónde busca el sistema los ejecutables |
| 3 | Rutas Windows vs. Unix | Separadores, mayúsculas, barras |
| 4 | Diagnóstico | Por qué 'command not found' y cómo resolverlo |
| 5 | Herencia del entorno | Por qué un cambio en un script no afecta a tu terminal |
Una variable de entorno es un par nombre-valor que el sistema operativo entrega a un proceso cuando lo arranca. Su propiedad fundamental, y la que explica casi todo su comportamiento desconcertante, es la herencia: cada proceso recibe una copia del entorno de quien lo lanzó, y los cambios que haga sobre esa copia afectan a él y a los procesos que él arranque después, pero jamás a su padre. De ahí que un script que exporta una variable no modifique la terminal desde la que lo lanzaste, y que para conseguirlo haya que ejecutarlo dentro de la sesión actual con source en lugar de como proceso aparte. No es una rareza del shell: es la semántica del entorno, y una vez entendida deja de sorprender que el cd de un script no cambie tu directorio actual. El valor de este mecanismo es que permite configurar un programa sin tocar su código ni sus argumentos, que es justamente lo que se necesita cuando el mismo binario debe comportarse distinto en desarrollo y en producción.
El PATH es la variable con mayor impacto diario. Contiene una lista ordenada de carpetas y el sistema la recorre en orden, deteniéndose en la primera coincidencia, cuando le pides ejecutar un comando por su nombre. De ese "en orden" y "la primera" se derivan dos consecuencias prácticas. La primera es el command not found: si el ejecutable existe pero su carpeta no está en la lista, el sistema no lo encuentra, exactamente igual que no encontrarías un libro que está en una estantería que no miraste. El programa no está roto, está fuera del alcance de la búsqueda. La segunda es más sutil y causa más horas perdidas: el enmascaramiento. Si dos carpetas del PATH contienen un ejecutable con el mismo nombre, gana la que aparece antes. Así es como acabas ejecutando el Python del sistema cuando creías estar usando el de tu gestor de versiones, o una herramienta vieja que quedó en /usr/local/bin sobre la nueva. La pregunta correcta ante cualquier duda no es "¿está instalado?" sino "¿cuál se está ejecutando?", y se responde preguntándole al propio sistema con which en Unix o Get-Command en PowerShell, que muestran la ruta que ganó la búsqueda. Todos los gestores de versiones de la clase 032 —pyenv, nvm, rustup, sdkman— funcionan manipulando este orden: no reinstalan nada, solo colocan delante la carpeta de la versión que has activado.
Las rutas difieren entre sistemas más de lo que parece, y no solo en el símbolo. Unix separa componentes con / y las entradas del PATH con :; Windows usa \ y separa el PATH con ;. Esa diferencia de separador de lista no es arbitraria: en Windows los dos puntos ya estaban ocupados por la letra de unidad (C:), así que hubo que elegir otro carácter. La barra invertida trae un problema añadido en el mundo Unix, donde \ es el carácter de escape, y por eso las rutas de Windows escritas en cadenas de código suelen requerir duplicarla o usar literales sin escape. Hay otras dos asimetrías que muerden en la práctica. Una es que los sistemas de archivos de Unix distinguen mayúsculas y minúsculas mientras que los de Windows normalmente no: un import Utils que funciona en tu portátil puede fallar en el servidor Linux porque el archivo se llama utils.py, y es un fallo que solo aparece en integración continua. La otra afecta a cómo se ejecuta un programa del directorio actual: en Unix hay que escribir ./main porque el directorio actual no está en el PATH por diseño —incluirlo permitiría que un ejecutable malicioso dejado en una carpeta compartida se ejecutara al teclear un nombre común—, mientras que Windows sí busca primero en el directorio actual y además consulta la variable PATHEXT para saber qué extensiones considera ejecutables.
Conviene cerrar con el uso más habitual de las variables de entorno fuera del PATH: la configuración y los secretos. Sacar una clave de API o una cadena de conexión del código y ponerla en el entorno es una buena práctica sólida, porque separa la configuración —que cambia entre entornos— del programa, que no debería cambiar, y evita que una credencial acabe en el repositorio, donde vivirá para siempre en el historial aunque la borres después. Pero conviene no exagerar la garantía: el entorno de un proceso es legible por ese proceso y por todos sus hijos, y a menudo inspeccionable desde fuera por el propio usuario. Es mucho mejor que codificar el secreto, y bastante menos que un gestor de secretos dedicado. Ese matiz es la diferencia entre aplicar una práctica y entenderla.
Instalas una herramienta, abres la terminal, escribes su nombre y el sistema responde command not found. El instinto lleva a desinstalar y reinstalar, o a sospechar que la descarga se corrompió. Casi nunca es eso. El ejecutable está donde el instalador lo dejó; lo que falta es que esa carpeta figure en el PATH, así que el sistema busca en las estanterías que conoce, no lo encuentra y lo dice. La prueba es inmediata: si invocas el programa por su ruta completa, funciona. Ahí queda claro que el problema no era el programa sino la búsqueda.
La variante más traicionera de esta escena es la contraria: el comando sí se encuentra, pero es el equivocado. Escribes python --version y obtienes una versión que no instalaste tú, porque otra carpeta del PATH aparece antes y contiene otro ejecutable con el mismo nombre. Aquí no hay ningún mensaje de error que te avise: el sistema hizo exactamente lo que le pediste, ejecutar el primer python que encontró. Por eso la herramienta de diagnóstico más rentable de toda esta clase es preguntar por la ruta resuelta antes que por la versión, y por eso el orden del PATH merece tratarse como configuración deliberada y no como una acumulación de líneas que fueron añadiendo los instaladores.
Ver y usar variables de entorno y el PATH:
Unix (bash):
echo $PATH # ver el PATH
export API_KEY="abc123" # definir una variable
Windows (PowerShell):
$env:PATH # ver el PATH
$env:API_KEY = "abc123" # definir una variable
Hay tres cosas que conviene notar en estas cuatro líneas. La primera es que la sintaxis difiere pero el modelo es idéntico: en ambos sistemas existe un diccionario de nombre a valor asociado al proceso, y ambos ofrecen una forma de leerlo y otra de escribirlo. La segunda es qué significa exactamente export en bash: sin él, la variable existe solo para el shell actual y no se hereda a los programas que lances; con él, pasa a formar parte del entorno que se copia a cada proceso hijo. Esa distinción entre variable de shell y variable de entorno explica muchos "pero si la definí" que terminan en desconcierto.
La tercera es que ambos comandos son temporales y solo afectan a esa sesión. Al cerrar la terminal, la variable desaparece, porque el proceso que la contenía murió y con él su copia del entorno. Para que persista hay que escribirla donde se construye el entorno de cada nueva sesión: los archivos de perfil del shell en Unix, el perfil de PowerShell o la configuración de variables del usuario en Windows. Y esa temporalidad, lejos de ser un inconveniente, es la forma más limpia de fijar configuración para una sola ejecución sin contaminar el sistema, que es exactamente lo que hacen los sistemas de integración continua al inyectar sus secretos.
Muestra tu PATH (echo $PATH o $env:PATH), cuenta cuántas carpetas incluye y comprueba si está la del lenguaje que instalaste. Después haz el ejercicio de diagnóstico completo, que es lo que de verdad se transfiere:
which python (Unix) o Get-Command python (PowerShell) y anota la ruta exacta que devuelve. Compárala con la que esperabas.which -a python lista todos los candidatos en orden; el primero es el que gana. Si hay más de uno, ya sabes por qué a veces obtienes una versión inesperada.| Síntoma / mensaje | Causa y cómo arreglar |
|---|---|
command not found justo después de instalar algo |
La carpeta del ejecutable no está en el PATH. Añádela, o comprueba que funciona invocándolo por su ruta completa |
| El comando existe pero responde una versión inesperada | Otra carpeta del PATH aparece antes y contiene un ejecutable homónimo. Resuelve con which -a o Get-Command y reordena el PATH |
| La variable que definí "no le llega" al programa | En bash, sin export es variable de shell, no de entorno, y no se hereda. Expórtala |
| El script cambia el PATH o el directorio y al volver nada cambió | Un proceso hijo no puede modificar el entorno del padre. Ejecútalo con source para que corra en la sesión actual |
| Funciona en local y falla en el servidor Linux por un archivo "que existe" | Unix distingue mayúsculas y minúsculas y Windows normalmente no. Unifica los nombres y respétalos siempre |
Rutas con \ o / escritas a mano en el código |
Cada sistema usa un separador distinto y \ además escapa en muchas cadenas. Usa la biblioteca de rutas del lenguaje y rutas relativas |
| Una credencial acaba subida al repositorio | Estaba escrita en el código. Léela del entorno y no la incluyas nunca en el control de versiones |
./main y en Windows no? Porque en Unix el directorio actual no está en el PATH deliberadamente. Si lo estuviera, bastaría con dejar un ejecutable malicioso llamado como un comando habitual en una carpeta compartida para que alguien lo ejecutara sin darse cuenta. Windows sí busca primero en el directorio actual, herencia de su tradición de sistema monousuario.: en Unix y ; en Windows? Porque en Windows los dos puntos ya estaban ocupados por la letra de unidad (C:), así que hacía falta otro carácter. Es una consecuencia de decisiones históricas de cada sistema, no de una preferencia estética./usr/bin deja la terminal casi inservible. La regla es siempre extender el valor existente, nunca reemplazarlo, y probar en una sesión temporal antes de hacerlo permanente.⏮️ Clase 039 · 📂 Parte · 📚 Índice · 🌐 Atlas · Clase 041 ⏭️