← Terminal y Línea de Comandos en Linux Bloque 7 · Procesos y entorno · Tema 34 de 43

Tema 34

Variables de entorno y configuración de Bash

¿Cómo sabe la shell que cp está en /usr/bin? ¿Por qué cd sin argumentos lleva justo a tu directorio? ¿Quién decidió que el indicador muestre tu nombre, o que el historial guarde mil líneas? Detrás de cada una de esas conductas hay un dato con nombre, que se puede leer y se puede cambiar. A lo largo del curso fuimos dejando promesas para este tema —agregar un directorio a PATH, elegir el editor, hacer permanente un ajuste—. Aquí se cumplen todas, y la terminal deja de ser la que vino instalada para ser la tuya.

1. Variables de la shell

Ya usamos variables varias veces, sin detenernos en ellas: $HOME, $PATH, $USER, y alguna propia, como el nombre=Marta del tema 30. Empezamos por ordenar lo que se sabe.

Una variable es un nombre asociado a un valor, que la shell guarda en memoria. Se crea con nombre=valor, y su valor se obtiene anteponiendo un signo $ al nombre.

terminal, en ~/practica
$ cd ~/practica
$ ciudad=Rosario                     # crear: sin signo $, sin espacios
$ echo $ciudad                       # usar: con signo $
Rosario
$ echo "Vivo en $ciudad"
Vivo en Rosario
$ ciudad="San Miguel de Tucumán"     # con espacios: entre comillas
$ echo $ciudad
San Miguel de Tucumán
$ unset ciudad                       # eliminar
$ echo "[$ciudad]"
[]

La sustitución la hace la shell antes de ejecutar el comando, como con los comodines: echo nunca ve la palabra $ciudad, sino el texto que la reemplazó. Y una variable que no existe no produce error: vale una cadena vacía, como muestra la última línea.

ReglaMalQué ocurre
Sin espacios alrededor del signo igual.ciudad = RosarioLa shell busca un comando llamado ciudad: command not found.
Sin $ al asignar.$ciudad=SaltaSe sustituye primero, y se intenta ejecutar Rosario=Salta.
Entre comillas, si el valor tiene espacios.ciudad=San MiguelLa shell intenta ejecutar un comando llamado Miguel, y la variable no queda definida.
El nombre lleva letras, dígitos y guion bajo, y no empieza con un dígito.mi-ciudad=Saltacommand not found.

Son las mismas reglas que vimos para los alias en el tema 5. Los nombres distinguen mayúsculas de minúsculas: ciudad, Ciudad y CIUDAD son tres variables.

Cuando al nombre le sigue inmediatamente otro texto, la shell no sabe dónde termina. Las llaves lo delimitan:

terminal
$ moneda=peso
$ echo "Cuesta diez $monedas"        # busca una variable llamada «monedas»
Cuesta diez 
$ echo "Cuesta diez ${moneda}s"
Cuesta diez pesos

De las comillas vale lo aprendido en el tema 9: dentro de las dobles las variables se sustituyen; dentro de las simples, no. Y conviene escribir siempre "$variable", entre comillas dobles, cuando el valor puede contener espacios, para que llegue al comando como un solo argumento.

2. El entorno de un proceso

La variable ciudad existe en esta shell, y en ningún otro lado. Hay otra clase de variables que tienen un alcance mayor, y son las que dan nombre al tema.

El entorno de un proceso es un conjunto de variables que recibe al nacer, como copia de las de su padre. Las variables de entorno son la manera en que una configuración llega a todos los programas sin tener que pasársela a cada uno.

En el tema 33 dijimos que un proceso hereda de su padre el dueño, el directorio actual y los canales estándar. El entorno es la cuarta herencia. Tu shell lo recibió del programa de terminal, que lo recibió de tu sesión; y cada comando que ejecutas lo recibe de la shell. Se lo ve con env:

terminal
$ env | wc -l
52
$ env | grep -E '^(HOME|USER|SHELL|LANG|TERM|PWD)=' | sort
HOME=/home/ana
LANG=es_AR.UTF-8
PWD=/home/ana/practica
SHELL=/bin/bash
TERM=xterm-256color
USER=ana
$ printenv HOME                      # el valor de una sola
/home/ana

Cada línea tiene la forma NOMBRE=valor. Por convención, las variables de entorno se escriben en mayúsculas, y las propias de uso momentáneo, en minúsculas: así no se pisa por accidente una del sistema.

La utilidad del mecanismo se aprecia pensando en la alternativa. Decenas de programas necesitan saber cuál es tu directorio personal, en qué idioma hablarte o qué tipo de terminal tienes. En lugar de un archivo de configuración por programa, o de una opción en cada comando, el dato se pone una vez en el entorno y cada programa lo encuentra allí al arrancar.

ComandoMuestra
env, printenvLas variables de entorno: las que heredan los programas.
printenv NOMBREEl valor de una variable de entorno.
echo $NOMBREEl valor de una variable cualquiera, sea de entorno o no.
setTodas las variables de la shell, y además sus funciones. Es muy largo: set | less.
declare -p NOMBRECómo está definida una variable, y si está exportada.

El entorno de cualquier proceso propio se puede leer en /proc, como anticipamos en el tema 32: tr '\0' '\n' < /proc/$$/environ muestra el de la shell, tal como lo recibió al nacer.

3. export: de la shell al entorno

La variable ciudad que creamos no apareció en la lista de env. Una variable recién creada es local: pertenece a la shell y no se hereda. Lo comprobamos lanzando una shell hija con bash -c, que ejecuta un comando en un Bash nuevo y termina:

terminal
$ ciudad=Rosario
$ bash -c 'echo "En la hija: [$ciudad]"'
En la hija: []
$ export ciudad
$ bash -c 'echo "En la hija: [$ciudad]"'
En la hija: [Rosario]

export marca una variable para que forme parte del entorno: desde ese momento, todos los procesos que la shell lance reciben una copia.

La shell tiene dos variables: ciudad, local, y PAIS, exportada. Al lanzar un proceso hijo, este recibe una copia de PAIS y no recibe ciudad La shell ciudad=Rosario PAIS=Argentina Un proceso hijo (ciudad no llega) PAIS=Argentina copia local arriba; exportada abajo su entorno
Solo las variables exportadas pasan al hijo, y pasan como copia: lo que el hijo haga con la suya no modifica la del padre.

Lo habitual es crear y exportar en un solo paso:

Se escribeEfecto
nombre=valorCrea una variable local, o cambia el valor de una existente.
export NOMBREExporta una variable que ya existe.
export NOMBRE=valorLa crea y la exporta.
export -n NOMBREDeja de exportarla; la variable sigue existiendo.
export -pLista las exportadas.
unset NOMBRELa elimina.

Una variable exportada sigue exportada aunque se le cambie el valor: no hace falta repetir el export. Por eso PATH=…, sin más, alcanza para modificar PATH, que ya venía exportada.

¿Cuándo hace falta exportar? Cuando el dato es para los programas. Si la variable la usa solo la shell —para armar una línea de comandos, o porque es un ajuste de la propia shell, como HISTSIZE o PS1—, no hace falta. Si la tiene que leer un programa, como EDITOR o LANG, sí.

4. La herencia va en un solo sentido

La palabra «copia» de la figura tiene una consecuencia que conviene ver funcionando, porque explica varios comportamientos del sistema que parecen arbitrarios.

terminal: el hijo cambia su copia
$ echo $ciudad
Rosario
$ bash -c 'ciudad=Salta; echo "En la hija: $ciudad"'
En la hija: Salta
$ echo $ciudad                       # en la shell original, nada cambió
Rosario

Un proceso puede modificar su propio entorno y el que tendrán sus hijos. No puede modificar el de su padre, ni el de ningún otro proceso en marcha.

Lo que se observaLa explicación
Una variable exportada en una terminal no existe en otra ventana.Son dos shells hermanas: ninguna es hija de la otra.
Un script que hace cd o define variables no deja nada al terminar.Se ejecutó en una shell hija, que murió con sus cambios.
cd, export y umask son comandos internos.Deben actuar sobre la propia shell; un programa externo sería un hijo y no podría, como vimos en el tema 3.
Un cambio en la configuración no afecta a los programas ya abiertos.Recibieron su copia del entorno al arrancar.

La segunda fila tiene una salida, que ya usamos sin explicarla. El comando source archivo, que también se escribe con un punto, . archivo, lee los comandos de un archivo y los ejecuta en la shell actual, sin crear un hijo. Las variables, alias y cambios de directorio que haya dentro quedan. Es la razón por la que, en el tema 5, los alias nuevos se cargaban con source ~/.bashrc y no ejecutando el archivo.

5. Una variable para un solo comando

A veces se quiere que un comando, y solo ese, vea un valor distinto. No hace falta exportar y después deshacer: una asignación escrita delante del comando, en la misma línea, vale únicamente para él.

terminal
$ date
lun 12 oct 2026 22:05:11 -03
$ LC_ALL=C date                      # sin configuración regional: en inglés
Mon Oct 12 22:05:14 -03 2026
$ TZ=Asia/Tokyo date                 # la misma hora, en otro huso
mar 13 oct 2026 10:05:17 JST
$ echo "[$TZ]"                       # la shell no quedó modificada
[]

La variable se agrega al entorno de ese proceso y de ningún otro. Es la forma que usamos en el tema 26, con LC_ALL=C sort archivo, para pedirle a sort un orden estricto sin cambiar el idioma de toda la sesión.

Se escribeLa variable existe…
VAR=valorEn esta shell, y no en los programas.
export VAR=valorEn esta shell y en todo lo que lance de aquí en más.
VAR=valor comandoSolo en ese comando.
env -i comandoNinguna: ejecuta el comando con el entorno vacío. Sirve para probar cómo se comporta un programa sin tu configuración.

La diferencia entre la primera fila y la tercera es un solo carácter. VAR=valor comando, en una línea, es una variable para ese comando. VAR=valor; comando, con punto y coma, son dos comandos: una asignación local, que el segundo no verá.

6. Las variables que conviene conocer

De las cincuenta y tantas del entorno, y del centenar largo que maneja Bash, estas son las que explican el comportamiento diario de la terminal:

VariableContieneLa usa
HOMETu directorio personal.cd sin argumentos, y la virgulilla ~.
USER, LOGNAMETu nombre de usuario.Muchos programas, para saber quién eres.
PWD, OLDPWDEl directorio actual; el anterior.pwd; cd -.
SHELLTu shell de inicio de sesión.Los programas que abren una shell.
PATHLos directorios donde se buscan los comandos.La shell, en cada comando. Sección 7.
LANG, LC_*El idioma y las convenciones regionales.Todos: mensajes, fechas, orden alfabético, separador decimal.
TERMEl tipo de terminal.Los programas de pantalla completa, para saber cómo dibujar.
EDITOR, VISUALTu editor preferido.Los programas que necesitan abrir uno.
PAGERTu paginador preferido.man y otros, para mostrar textos largos.
TZEl huso horario, si se quiere uno distinto del sistema.date, ls -l.
PS1El formato del indicador.Bash. Sección 11.
HISTSIZE, HISTFILESIZE, HISTCONTROLEl tamaño y las reglas del historial.Bash, como vimos en el tema 5.

Tres de ellas saldan deudas de temas anteriores. EDITOR es la variable que mencionamos en el tema 13: la consultan crontab -e, visudo, git y cualquier otro programa que deba hacerte editar un texto. Con export EDITOR=nano, o vim, se termina la sorpresa de caer en un editor que no se esperaba.

LANG tiene la forma idioma_PAÍS.codificación: es_AR.UTF-8 es español de Argentina. Las variables LC_ ajustan aspectos por separado —LC_TIME las fechas, LC_NUMERIC los números, LC_MESSAGES los mensajes—, y LC_ALL las anula a todas de una vez. El valor C significa «ninguna convención regional»: inglés, y orden por código de carácter. Es el recurso para reproducir un problema tal como lo vería cualquiera, o para buscar en Internet un mensaje de error en su versión original.

Además de las variables con nombre, la shell mantiene unos parámetros especiales que fuimos encontrando. No se les puede asignar un valor: los actualiza ella.

ParámetroContieneVisto en
$?El código de salida del último comando.Tema 25
$$El PID de la shell.Tema 32
$!El PID del último trabajo lanzado en segundo plano.Tema 33
$RANDOMUn número al azar entre 0 y 32767, distinto cada vez.—
$SECONDSLos segundos transcurridos desde que se abrió la shell.—

7. PATH: agregar un directorio propio

En el tema 3 vimos qué es PATH: la lista de directorios, separados por dos puntos, donde la shell busca cada comando externo, en orden y hasta la primera coincidencia. Quedó pendiente cómo modificarla. La ocasión típica es tener programas propios y querer ejecutarlos por su nombre, desde cualquier lugar.

Creamos un directorio para ellos, y dentro un programa mínimo: un archivo de texto con dos líneas y permiso de ejecución, como el del tema 18. De los scripts nos ocupamos en el tema 41.

terminal
$ mkdir ~/bin
$ echo '#!/bin/bash' > ~/bin/saludo
$ echo 'echo "Hola, $USER. Estás en $PWD."' >> ~/bin/saludo
$ chmod +x ~/bin/saludo
$ saludo
saludo: command not found
$ ~/bin/saludo                       # con la ruta, funciona
Hola, ana. Estás en /home/ana/practica.

El programa existe y funciona, pero la shell no lo encuentra por su nombre: /home/ana/bin no está en la lista. Lo agregamos:

terminal
$ PATH="$HOME/bin:$PATH"
$ echo $PATH
/home/ana/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
$ saludo
Hola, ana. Estás en /home/ana/practica.
$ type saludo
saludo is /home/ana/bin/saludo
la asignación, leída
PATH="$HOME/bin:$PATH"
       │        │  │
       │        │  └─ el valor que PATH tenía hasta ahora
       │        └─ el separador
       └─ el directorio nuevo

El nuevo valor se arma con el anterior: «mi directorio, dos puntos, y todo lo que ya había». El lugar donde se pone lo nuevo decide quién gana cuando hay dos programas con el mismo nombre:

FormaEl directorio quedaConsecuencia
PATH="$HOME/bin:$PATH"Al principio.Tus programas tienen precedencia sobre los del sistema que se llamen igual.
PATH="$PATH:$HOME/bin"Al final.Los del sistema tienen precedencia. Es la opción más prudente.
Olvidar el $PATH deja la terminal sin comandos

Si se escribe PATH="$HOME/bin", sin el valor anterior, la lista pasa a tener un solo directorio. De inmediato ls, cat, nano y todo lo demás responden command not found: los programas siguen en el disco, pero la shell ya no sabe dónde buscarlos. El daño se limita a esa terminal y no es grave. Se arregla escribiendo una lista mínima a mano, PATH=/usr/local/bin:/usr/bin:/bin, o cerrando la ventana y abriendo otra, que nace con el valor correcto.

Dos precauciones más. No conviene agregar a PATH el directorio actual, .: es la medida de seguridad que explicamos en el tema 3, y vale también para los directorios donde otros pueden escribir, como /tmp. Y la shell recuerda dónde encontró cada comando, para no buscarlo de nuevo: si mueves un programa de un directorio a otro y sigue buscándolo en el viejo, hash -r le hace olvidar lo aprendido.

Como toda asignación hecha en la línea de comandos, esta dura lo que dure la shell. Para que sea permanente hay que escribirla en un archivo que Bash lea al empezar.

8. Los archivos de inicio

Los archivos de inicio son archivos de texto con comandos que Bash ejecuta automáticamente al arrancar, antes de mostrar el primer indicador. Todo lo que se puede escribir en la terminal se puede escribir en ellos.

No tienen un formato especial: son líneas de comandos, las mismas de este curso. La complicación está en que hay varios, y que Bash lee unos u otros según cómo se lo inició.

Tipo de shellCuándo ocurreLee, en orden
De inicio de sesión (login)Al identificarte: en una consola de texto, al entrar por ssh, con su -./etc/profile, y después el primero que exista de ~/.bash_profile, ~/.bash_login y ~/.profile.
Interactiva comúnAl abrir una ventana de terminal en el escritorio, o al escribir bash./etc/bash.bashrc y ~/.bashrc.
No interactivaAl ejecutar un script.Ninguno. Solo hereda el entorno.

Los que están en /etc valen para todos los usuarios y los administra root; los de tu directorio personal son tuyos. En Ubuntu vienen dos, copiados de /etc/skel al crear la cuenta, como vimos en el tema 20:

ArchivoSe leeEs el lugar para
~/.profileUna vez, al iniciar la sesión.Lo que debe heredar todo lo que se abra: variables de entorno exportadas, y PATH.
~/.bashrcEn cada terminal que se abre.Lo que es propio de la shell interactiva: alias, funciones, indicador, historial, opciones.
~/.bash_aliasesLo lee .bashrc, si existe.Los alias, aparte.
~/.bash_logoutAl cerrar una shell de inicio de sesión.Tareas de despedida: limpiar la pantalla.

La división responde a lo que se hereda y lo que no. Las variables exportadas pasan de la sesión a todas las terminales: alcanza con definirlas una vez, en .profile. Los alias y las funciones no se heredan, y cada shell tiene que definirlos de nuevo: por eso van en .bashrc.

El .profile de Ubuntu trae dos cortesías que conviene conocer. Si la shell es Bash, lee también ~/.bashrc, de modo que una sesión por ssh tiene los mismos alias que una terminal del escritorio. Y contiene estas líneas:

fragmento de ~/.profile, en Ubuntu
# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
    PATH="$HOME/bin:$PATH"
fi

Se lee sin saber programar: «si existe el directorio ~/bin, agrégalo al principio de PATH». Lo mismo hace con ~/.local/bin. En Ubuntu, entonces, no hay nada que configurar: el directorio ~/bin que creamos en la sección anterior quedará en PATH por sí solo a partir del próximo inicio de sesión.

Crear ~/.bash_profile anula ~/.profile

De los tres archivos personales de inicio de sesión, Bash lee solo el primero que encuentra, y ~/.bash_profile es el primero de la lista. Muchos tutoriales, escritos para otras distribuciones, indican crearlo; en Ubuntu eso deja sin efecto a ~/.profile, con todo lo que trae, y además a .bashrc en las sesiones de inicio. Lo que se quiera agregar va en el ~/.profile que ya existe.

9. Editar .bashrc sin romper nada

~/.bashrc es el archivo de configuración que más se toca, y es un archivo que se ejecuta cada vez que se abre una terminal. Un error de sintaxis en él aparece como un mensaje al comienzo de cada sesión; uno grave puede dejar la terminal incómoda de usar. El procedimiento seguro es el de siempre, y lleva un minuto.

  1. Guardar una copia: cp ~/.bashrc ~/.bashrc.bak. respaldar
  2. Editar con nano ~/.bashrc, agregando lo propio al final, bajo un comentario que lo identifique. editar
  3. Cargar el archivo en la shell actual: source ~/.bashrc. Si hay un error, aparece aquí, con el número de línea. aplicar
  4. Abrir una terminal nueva, sin cerrar la actual, y comprobar que arranca limpia. comprobar

El cuarto paso es la red de seguridad: mientras la terminal vieja siga abierta, hay desde dónde corregir. Y si algo saliera muy mal, bash --norc inicia una shell que no lee .bashrc, desde la que se puede restaurar la copia.

Antes de agregar nada conviene recorrer el archivo con less ~/.bashrc. El de Ubuntu tiene poco más de cien líneas, la mayoría comentarios, y casi todo se entiende con lo aprendido:

En el .bashrc de UbuntuQué hace
Una comprobación inicial, case $- in …Si la shell no es interactiva, abandona el archivo: nada de lo que sigue hace falta en un script.
HISTCONTROL=ignoreboth, HISTSIZE=1000, HISTFILESIZE=2000Los ajustes del historial del tema 5.
shopt -s histappend, shopt -s checkwinsizeOpciones de la shell. Sección 13.
Un bloque que define PS1El indicador, con colores si la terminal los admite.
alias ls='ls --color=auto', alias ll='ls -alF'…Los alias que venimos usando desde el principio.
if [ -f ~/.bash_aliases ]; then . ~/.bash_aliases; fiSi existe ~/.bash_aliases, lo carga. El punto es source.
Un bloque final sobre bash_completionActiva el completado con Tab para las opciones de los comandos.

Ahí estaba la respuesta a varias preguntas del curso: por qué ls muestra colores, de dónde sale ll, por qué el historial tiene mil líneas. Nada de eso es parte de Bash: son decisiones de la distribución, escritas en un archivo de texto que te pertenece.

10. Qué poner: ajustes, alias y funciones

Un bloque razonable para agregar al final de .bashrc, que reúne lo que quedó pendiente en temas anteriores:

al final de ~/.bashrc
# ---------- Mi configuración ----------

# El editor que abrirán los programas
export EDITOR=nano

# Un historial más largo, y con fecha y hora
HISTSIZE=5000
HISTFILESIZE=10000
HISTTIMEFORMAT='%F %T  '

# Alias
alias pr='cd ~/practica'
alias rm='rm -i'

# Crear un directorio y entrar en él
mkcd() {
    mkdir -p "$1" && cd "$1"
}

Las asignaciones del historial cumplen la promesa del tema 5; HISTTIMEFORMAT hace que history muestre cuándo se ejecutó cada línea. Si se quisiera que los archivos nuevos nazcan más cerrados, aquí iría también el umask del tema 18.

El último bloque es algo nuevo. Un alias reemplaza un texto por otro, y no puede poner un argumento en el medio: «crear un directorio y entrar en él» necesita usar el nombre dos veces. Para eso existen las funciones.

Una función es un grupo de comandos con nombre, que se ejecuta como si fuera un comando. Dentro de ella, $1, $2… son los argumentos con que se la llamó.

terminal, después de source ~/.bashrc
$ mkcd /tmp/proyecto/datos
$ pwd
/tmp/proyecto/datos
$ type mkcd
mkcd is a function
mkcd ()
{
    mkdir -p "$1" && cd "$1"
}

El cd de la función surte efecto porque una función se ejecuta en la shell actual, no en un proceso hijo. Hecho como script no habría funcionado, por la herencia de la sección 4.

AliasFunciónScript en ~/bin
EsUna abreviatura de textoComandos con nombre, en la shellUn archivo ejecutable
ArgumentosSolo al final$1, $2…, donde se quiera$1, $2…
Puede cambiar la shell (cd, variables)SíSíNo: es un proceso hijo
Disponible enLas shells interactivas que lo definanLas shells que la definanCualquier programa, cualquier shell
Conviene paraUn comando con sus opciones habitualesDos o tres comandos que usan un argumentoTodo lo que sea más largo

Las funciones, con sus condiciones y sus bucles, son el material del curso siguiente. Aquí alcanza con saber leer una y con tener dos o tres propias.

11. PS1: el indicador

Lo que la shell escribe antes de cada comando no es fijo: es el valor de una variable, PS1, de prompt string 1. En los ejemplos del curso lo abreviamos como $; el verdadero, en Ubuntu, tiene esta forma:

anatomía del indicador de Ubuntu
ana@equipo:~/practica$
 │    │       │     │
 │    │       │     └─ $ para un usuario común; # para root
 │    │       └─ el directorio actual, con ~ en lugar de /home/ana
 │    └─ el nombre de la máquina
 └─ el usuario

Cada dato tiene su razón. En una máquina propia, el usuario y el equipo parecen redundantes; con tres terminales abiertas hacia tres servidores, son lo que evita ejecutar un comando en el lugar equivocado. Y el signo final avisa cuándo se trabaja como root, según vimos en el tema 19.

Dentro de PS1, esos datos se piden con códigos que empiezan con una barra invertida, y que Bash reemplaza cada vez que dibuja el indicador:

CódigoSe reemplaza porEjemplo
\uEl nombre del usuario.ana
\hEl nombre de la máquina.equipo
\wEl directorio actual, completo.~/practica/fotos
\WSolo el último componente del directorio.fotos
\$$, o # si eres root.$
\A, \tLa hora: con minutos; con segundos.22:15, 22:15:08
\dLa fecha.lun oct 12
\jLa cantidad de trabajos de la shell.2
\!El número que tendrá el comando en el historial.512
\nUn salto de línea.

Como PS1 es una variable, se la puede cambiar en el momento y ver el resultado en la línea siguiente. Antes guardamos el valor original, para poder volver:

terminal: probando indicadores
ana@equipo:~/practica$ original=$PS1
ana@equipo:~/practica$ PS1='\W \$ '
practica $ PS1='\A \u:\w\$ '
22:15 ana:~/practica$ PS1='[\u@\h \w]\n\$ '
[ana@equipo ~/practica]
$ PS1='[$?] \W \$ '
[0] practica $ ls /noexiste
ls: cannot access '/noexiste': No such file or directory
[2] practica $ PS1=$original
ana@equipo:~/practica$ 

Cuatro estilos, cada uno con su uso. El primero es mínimo, bueno para terminales angostas. El segundo agrega la hora, que en una sesión larga deja constancia de cuándo se ejecutó cada cosa. El tercero ocupa dos líneas: los datos arriba, y abajo el renglón entero libre para escribir, lo que conviene cuando las rutas son largas. El cuarto muestra el código de salida del comando anterior: cero después de un acierto, y el código del error después de un fallo.

Tres detalles de escritura. El valor va entre comillas simples, para que $? y \$ lleguen sin tocar a la variable y se resuelvan cada vez que se dibuja el indicador, no una sola vez al asignar. Conviene terminar con un espacio, para que lo que se escribe no quede pegado. Y PS1 no se exporta: es un ajuste de la shell, que otros programas no leen.

Hay un segundo indicador, PS2, que ya vimos muchas veces: es el > que aparece cuando una línea queda sin terminar, por unas comillas abiertas o un documento en línea.

12. Un indicador con colores

El indicador de Ubuntu aparece en verde y azul. El color no es una propiedad de la terminal: se obtiene con unas secuencias de caracteres, incluidas en PS1, que la terminal interpreta como órdenes en lugar de mostrarlas.

anatomía de una secuencia de color
\[\e[1;32m\]
│  │  │ │ │
│  │  │ │ └─ fin de la parte que no ocupa lugar en pantalla
│  │  │ └─ 32: color verde
│  │  └─ 1: negrita
│  └─ \e[ : comienzo de la orden para la terminal
└─ comienzo de una parte que no ocupa lugar en pantalla
CódigoEfectoCódigoEfecto
0Volver a lo normal1Negrita
30Negro34Azul
31Rojo35Magenta
32Verde36Cian
33Amarillo37Blanco

Una secuencia de color es un interruptor: todo lo que sigue sale en ese color hasta que otra lo cambie. Por eso cada tramo coloreado se cierra con \[\e[0m\], que restablece el aspecto normal; sin ella, el color se extiende a lo que se escribe y a la salida de los comandos. Con eso se puede leer el indicador de Ubuntu, que en su parte esencial es este:

el indicador de Ubuntu, por partes
\[\e[1;32m\]     verde, en negrita
\u@\h            ana@equipo
\[\e[0m\]        normal
:                dos puntos
\[\e[1;34m\]     azul, en negrita
\w               ~/practica
\[\e[0m\]        normal
\$               $ y un espacio final

Y se puede escribir uno propio. Este muestra la hora en cian, el directorio en amarillo y el signo final normal:

terminal
$ PS1='\[\e[36m\]\A\[\e[0m\] \[\e[1;33m\]\w\[\e[0m\] \$ '
22:20 ~/practica $ 
Los corchetes \[ y \] no son decoración

Bash necesita saber cuántos caracteres ocupa el indicador en la pantalla, para calcular dónde termina el renglón. Las secuencias de color son caracteres que no ocupan lugar, y hay que decírselo encerrándolas entre \[ y \]. Si faltan, Bash cuenta de más: las líneas largas se pliegan antes de tiempo, se escriben sobre sí mismas, y al recorrer el historial con la flecha quedan restos de comandos anteriores. Es el error clásico de los indicadores con color.

Cuando el indicador elegido convence, se lo hace permanente agregando la línea PS1='…' al final de ~/.bashrc, después del bloque de Ubuntu que define el suyo, con el procedimiento de la sección 9.

13. Opciones de la shell: set y shopt

Además de variables, Bash tiene interruptores: conductas que se activan o se desactivan. Por razones históricas están repartidos en dos comandos internos.

set -oshopt
Activarset -o opciónshopt -s opción
Desactivarset +o opciónshopt -u opción
Listarset -oshopt

En set, el signo menos activa y el más desactiva, al revés de lo que se esperaría. Las opciones que más se aprovechan en el uso interactivo:

OpciónCon ella activada…
set -o noclobberLa redirección > se niega a sobrescribir un archivo existente. Para hacerlo a propósito se escribe >|.
set -o viLa línea de comandos se edita con las teclas de vim, en lugar de las del tema 5.
shopt -s histappendAl cerrar, la shell agrega su historial al archivo en vez de reemplazarlo. Ubuntu la trae activada.
shopt -s autocdEscribir el nombre de un directorio alcanza para entrar en él, sin cd.
shopt -s cdspellcd corrige errores leves de tipeo en el nombre del directorio.
shopt -s globstarEl comodín ** coincide con archivos en todos los subdirectorios: ls **/*.txt.
shopt -s nocaseglobLos comodines no distinguen mayúsculas de minúsculas.
terminal
$ set -o noclobber
$ echo prueba > tareas.txt
bash: tareas.txt: cannot overwrite existing file
$ set +o noclobber
$ shopt -s globstar
$ ls ~/bin/**
/home/ana/bin/saludo

noclobber es la protección contra el accidente del tema 23: vaciar un archivo valioso por escribir > donde iba >>. Como todo lo de este tema, las opciones valen para la shell en que se las activa; las que se quieran conservar se escriben en .bashrc.

14. Recetas y errores comunes

Quiero…Comando
Ver el valor de una variableecho "$NOMBRE"
Ver todas las variables de entornoenv | sort | less
Saber si una variable está exportadadeclare -p NOMBRE
Que los programas vean una variableexport NOMBRE=valor
Darle un valor a un solo comandoNOMBRE=valor comando
Ver un mensaje de error en inglésLC_ALL=C comando
Ver PATH con un directorio por líneaecho "$PATH" | tr ':' '\n'
Agregar un directorio a PATHPATH="$PATH:/ruta/del/directorio"
Saber qué archivo se ejecuta al escribir un comandotype -a comando
Elegir el editor por defectoexport EDITOR=nano, en ~/.bashrc
Aplicar los cambios hechos en .bashrcsource ~/.bashrc
Volver al .bashrc original de la distribucióncp /etc/skel/.bashrc ~/.bashrc
Abrir una shell sin mi configuraciónbash --norc
Probar un indicadorPS1='\A \w \$ '
SíntomaCausaSolución
ciudad: command not found al asignar.Hay espacios alrededor del signo igual.ciudad=Rosario
Un programa o un script no ve la variable.Es local: no se exportó.export NOMBRE
La variable no existe en otra terminal.Cada shell tiene sus propias variables.Definirla en un archivo de inicio.
Un script cambió una variable o hizo cd, y no quedó nada.Se ejecutó en un proceso hijo.source script, o una función.
De pronto todo responde command not found.Se asignó PATH sin incluir $PATH.PATH=/usr/local/bin:/usr/bin:/bin, o una terminal nueva.
Mi programa de ~/bin no se encuentra.El directorio no está en PATH, o al archivo le falta el permiso de ejecución.echo $PATH; chmod +x.
Se ejecuta otra versión del programa, no la mía.Hay otro con el mismo nombre antes en PATH.type -a nombre; cambiar el orden.
Edité .bashrc y no cambió nada.Bash lo lee solo al arrancar.source ~/.bashrc
Edité .profile y no cambió nada.Se lee solo al iniciar la sesión.Cerrar la sesión y volver a entrar.
Cada terminal nueva empieza con un mensaje de error.Un error de sintaxis en .bashrc.El mensaje indica la línea; o restaurar la copia.
PATH tiene el mismo directorio repetido muchas veces.Se lo agrega en .bashrc, que se lee en cada shell anidada.Agregarlo en ~/.profile.
Las líneas largas se pliegan mal desde que cambié el indicador.Faltan \[ y \] alrededor de los colores.Encerrar cada secuencia.
El indicador muestra siempre el mismo dato.PS1 se asignó entre comillas dobles.Comillas simples.
Con sudo, el comando no ve mi variable.sudo limpia el entorno, por seguridad.sudo NOMBRE=valor comando
Un alias funciona en la terminal y no en un script.Los alias son de las shells interactivas.Escribir el comando completo.

La referencia del tema:

Se escribeQué hace
nombre=valorCrea o modifica una variable de la shell.
$nombre, ${nombre}Se reemplaza por su valor.
unset nombreLa elimina.
export NOMBRE=valorLa pone en el entorno: la heredan los programas.
env, printenvMuestran el entorno.
NOMBRE=valor comandoLa define solo para ese comando.
PATH="$PATH:directorio"Agrega un directorio a la búsqueda de comandos.
source archivo, . archivoEjecuta un archivo en la shell actual.
~/.profileSe lee al iniciar la sesión: entorno y PATH.
~/.bashrcSe lee en cada terminal: alias, funciones, indicador.
nombre() { comandos; }Define una función.
PS1='\u@\h:\w\$ 'Define el indicador.
\[\e[1;32m\] … \[\e[0m\]Color en el indicador, y vuelta a lo normal.
set -o opción, shopt -s opciónActivan una opción de la shell.

Queda un directorio nuevo, ~/bin, con un programa propio; lo usaremos de nuevo en el tema 41. Con este tema termina el bloque: sabemos ver los procesos, gobernarlos y configurar el entorno en que nacen. El bloque siguiente se ocupa del sistema como conjunto, y empieza por lo primero que se hace en una instalación nueva. En el tema 35 aprendemos a instalar, actualizar y quitar programas con apt.

15. Resumen

16. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Por qué falla nombre = Ana?

Porque los espacios hacen que la shell lo tome como un comando llamado nombre con dos argumentos. La asignación se escribe sin espacios: nombre=Ana.

2. ¿Qué muestra echo "${fruta}s" si fruta vale pera? ¿Y echo "$frutas"?

El primero muestra peras. El segundo, una línea vacía: busca una variable llamada frutas, que no existe.

3. ¿Qué es el entorno de un proceso?

El conjunto de variables que recibe al nacer, como copia de las de su padre, y que puede consultar para ajustar su comportamiento.

4. ¿Qué diferencia hay entre dato=5 y export dato=5?

La primera crea una variable local, que solo existe en la shell. La segunda la pone además en el entorno, de modo que la heredan los programas que la shell lance.

5. Exportas una variable en una terminal y no aparece en otra que ya estaba abierta. ¿Por qué?

Porque son dos shells independientes. El entorno se hereda de padre a hijo, y ninguna es hija de la otra.

6. Un script contiene cd /tmp. Al terminar, la shell sigue en el mismo directorio. ¿Por qué, y cómo se evita?

Porque el script se ejecutó en un proceso hijo, que cambió su propio directorio y terminó. Con source script se ejecuta en la shell actual, y el cambio queda.

7. ¿Qué hace LC_ALL=C ls -l, y cuánto dura el cambio?

Ejecuta ls -l con la variable LC_ALL en C, es decir, sin convenciones regionales. La variable existe solo para ese comando.

8. ¿Para qué sirve la variable EDITOR?

Indica qué editor deben abrir los programas que necesitan que el usuario edite un texto.

9. ¿Qué diferencia hay entre PATH="$HOME/bin:$PATH" y PATH="$PATH:$HOME/bin"?

La primera pone el directorio al principio de la lista, de modo que sus programas tienen precedencia. La segunda lo pone al final, y prevalecen los del sistema.

10. Escribiste PATH=~/bin y ahora ls responde command not found. ¿Qué pasó, y cómo se arregla?

Se reemplazó toda la lista por un solo directorio. Se arregla asignando PATH=/usr/local/bin:/usr/bin:/bin, o abriendo otra terminal.

11. ¿Cuándo se lee ~/.profile y cuándo ~/.bashrc?

~/.profile, una vez, al iniciar la sesión. ~/.bashrc, cada vez que se abre una shell interactiva, por ejemplo una ventana de terminal.

12. ¿Por qué los alias van en .bashrc y no en .profile?

Porque no se heredan: cada shell debe definirlos de nuevo, y .bashrc es el archivo que se lee en cada una.

13. ¿Para qué sirve source ~/.bashrc?

Para que la shell actual lea de nuevo el archivo y aplique los cambios, sin abrir otra terminal.

14. ¿Qué indicador produce PS1='\u en \W \$ ' para ana en /home/ana/practica?

ana en practica $

15. ¿Para qué sirven \[ y \] en PS1?

Encierran las secuencias que no ocupan lugar en la pantalla, como las de color, para que Bash calcule bien el largo del indicador y no pliegue mal las líneas.

Para practicar

  1. Crea una variable curso con el valor Terminal de Linux. Muéstrala dentro de una frase, entre comillas dobles y entre comillas simples. Elimínala y vuelve a mostrarla.
  2. Cuenta cuántas variables de entorno tienes. Lista las que empiezan con LC_ o LANG, y las que contienen tu nombre de usuario en su valor.
  3. Repite la experiencia de la sección 3 con una variable propia: comprueba con bash -c que no se hereda, expórtala, comprueba que sí, y verifica su estado con declare -p antes y después.
  4. Ejecuta ls -l, LC_ALL=C ls -l y date, LC_ALL=C date. ¿Qué cambia? Prueba también TZ=Europe/Madrid date.
  5. Muestra PATH con un directorio por línea y numerado. Averigua con type -a en cuál está python3, y cuántos de esos directorios existen realmente.
  6. Crea ~/bin y, dentro, un programa ahora que muestre la fecha y la hora con el formato que prefieras. Agrégalo a PATH y ejecútalo desde /tmp. Luego abre una terminal nueva: ¿sigue funcionando? ¿Por qué?
  7. Haz una copia de ~/.bashrc y recórrelo con less. Localiza la definición del historial, la de PS1 y la línea que lee .bash_aliases.
  8. Siguiendo los cuatro pasos de la sección 9, agrega a .bashrc tu editor preferido, un historial de 5000 líneas con fecha y la función mkcd. Compruébalo en una terminal nueva con echo $EDITOR, history | tail -n 3 y type mkcd.
  9. Diseña un indicador que muestre la hora, tu usuario y el directorio en tres colores distintos, en dos líneas. Pruébalo con una línea de comandos larga, para verificar que se pliega bien, y agrégalo a .bashrc.
  10. Activa noclobber e intenta sobrescribir un archivo de /tmp con >, con >> y con >|. Luego prueba shopt -s autocd y shopt -s cdspell, y decide cuáles dejarías activadas.