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.
$ 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.
| Regla | Mal | Qué ocurre |
|---|---|---|
| Sin espacios alrededor del signo igual. | ciudad = Rosario | La shell busca un comando llamado ciudad: command not found. |
Sin $ al asignar. | $ciudad=Salta | Se sustituye primero, y se intenta ejecutar Rosario=Salta. |
| Entre comillas, si el valor tiene espacios. | ciudad=San Miguel | La 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=Salta | command 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:
$ 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:
$ 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.
| Comando | Muestra |
|---|---|
env, printenv | Las variables de entorno: las que heredan los programas. |
printenv NOMBRE | El valor de una variable de entorno. |
echo $NOMBRE | El valor de una variable cualquiera, sea de entorno o no. |
set | Todas las variables de la shell, y además sus funciones. Es muy largo: set | less. |
declare -p NOMBRE | Có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:
$ 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.
Lo habitual es crear y exportar en un solo paso:
| Se escribe | Efecto |
|---|---|
nombre=valor | Crea una variable local, o cambia el valor de una existente. |
export NOMBRE | Exporta una variable que ya existe. |
export NOMBRE=valor | La crea y la exporta. |
export -n NOMBRE | Deja de exportarla; la variable sigue existiendo. |
export -p | Lista las exportadas. |
unset NOMBRE | La 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.
$ 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 observa | La 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.
$ 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 escribe | La variable existe… |
|---|---|
VAR=valor | En esta shell, y no en los programas. |
export VAR=valor | En esta shell y en todo lo que lance de aquí en más. |
VAR=valor comando | Solo en ese comando. |
env -i comando | Ninguna: 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:
| Variable | Contiene | La usa |
|---|---|---|
HOME | Tu directorio personal. | cd sin argumentos, y la virgulilla ~. |
USER, LOGNAME | Tu nombre de usuario. | Muchos programas, para saber quién eres. |
PWD, OLDPWD | El directorio actual; el anterior. | pwd; cd -. |
SHELL | Tu shell de inicio de sesión. | Los programas que abren una shell. |
PATH | Los 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. |
TERM | El tipo de terminal. | Los programas de pantalla completa, para saber cómo dibujar. |
EDITOR, VISUAL | Tu editor preferido. | Los programas que necesitan abrir uno. |
PAGER | Tu paginador preferido. | man y otros, para mostrar textos largos. |
TZ | El huso horario, si se quiere uno distinto del sistema. | date, ls -l. |
PS1 | El formato del indicador. | Bash. Sección 11. |
HISTSIZE, HISTFILESIZE, HISTCONTROL | El 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ámetro | Contiene | Visto 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 |
$RANDOM | Un número al azar entre 0 y 32767, distinto cada vez. | — |
$SECONDS | Los 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.
$ 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:
$ 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
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:
| Forma | El directorio queda | Consecuencia |
|---|---|---|
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. |
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 shell | Cuándo ocurre | Lee, 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ún | Al abrir una ventana de terminal en el escritorio, o al escribir bash. | /etc/bash.bashrc y ~/.bashrc. |
| No interactiva | Al 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:
| Archivo | Se lee | Es el lugar para |
|---|---|---|
~/.profile | Una vez, al iniciar la sesión. | Lo que debe heredar todo lo que se abra: variables de entorno exportadas, y PATH. |
~/.bashrc | En cada terminal que se abre. | Lo que es propio de la shell interactiva: alias, funciones, indicador, historial, opciones. |
~/.bash_aliases | Lo lee .bashrc, si existe. | Los alias, aparte. |
~/.bash_logout | Al 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:
# 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.
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.
- Guardar una copia:
cp ~/.bashrc ~/.bashrc.bak. respaldar - Editar con
nano ~/.bashrc, agregando lo propio al final, bajo un comentario que lo identifique. editar - Cargar el archivo en la shell actual:
source ~/.bashrc. Si hay un error, aparece aquí, con el número de línea. aplicar - 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 Ubuntu | Qué 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=2000 | Los ajustes del historial del tema 5. |
shopt -s histappend, shopt -s checkwinsize | Opciones de la shell. Sección 13. |
Un bloque que define PS1 | El 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; fi | Si existe ~/.bash_aliases, lo carga. El punto es source. |
Un bloque final sobre bash_completion | Activa 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:
# ---------- 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ó.
$ 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.
| Alias | Función | Script en ~/bin | |
|---|---|---|---|
| Es | Una abreviatura de texto | Comandos con nombre, en la shell | Un archivo ejecutable |
| Argumentos | Solo al final | $1, $2…, donde se quiera | $1, $2… |
Puede cambiar la shell (cd, variables) | Sí | Sí | No: es un proceso hijo |
| Disponible en | Las shells interactivas que lo definan | Las shells que la definan | Cualquier programa, cualquier shell |
| Conviene para | Un comando con sus opciones habituales | Dos o tres comandos que usan un argumento | Todo 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:
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ódigo | Se reemplaza por | Ejemplo |
|---|---|---|
\u | El nombre del usuario. | ana |
\h | El nombre de la máquina. | equipo |
\w | El directorio actual, completo. | ~/practica/fotos |
\W | Solo el último componente del directorio. | fotos |
\$ | $, o # si eres root. | $ |
\A, \t | La hora: con minutos; con segundos. | 22:15, 22:15:08 |
\d | La fecha. | lun oct 12 |
\j | La cantidad de trabajos de la shell. | 2 |
\! | El número que tendrá el comando en el historial. | 512 |
\n | Un 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:
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.
\[\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ódigo | Efecto | Código | Efecto |
|---|---|---|---|
0 | Volver a lo normal | 1 | Negrita |
30 | Negro | 34 | Azul |
31 | Rojo | 35 | Magenta |
32 | Verde | 36 | Cian |
33 | Amarillo | 37 | Blanco |
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:
\[\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:
$ PS1='\[\e[36m\]\A\[\e[0m\] \[\e[1;33m\]\w\[\e[0m\] \$ '
22:20 ~/practica $
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 -o | shopt | |
|---|---|---|
| Activar | set -o opción | shopt -s opción |
| Desactivar | set +o opción | shopt -u opción |
| Listar | set -o | shopt |
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ón | Con ella activada… |
|---|---|
set -o noclobber | La redirección > se niega a sobrescribir un archivo existente. Para hacerlo a propósito se escribe >|. |
set -o vi | La línea de comandos se edita con las teclas de vim, en lugar de las del tema 5. |
shopt -s histappend | Al cerrar, la shell agrega su historial al archivo en vez de reemplazarlo. Ubuntu la trae activada. |
shopt -s autocd | Escribir el nombre de un directorio alcanza para entrar en él, sin cd. |
shopt -s cdspell | cd corrige errores leves de tipeo en el nombre del directorio. |
shopt -s globstar | El comodín ** coincide con archivos en todos los subdirectorios: ls **/*.txt. |
shopt -s nocaseglob | Los comodines no distinguen mayúsculas de minúsculas. |
$ 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 variable | echo "$NOMBRE" |
| Ver todas las variables de entorno | env | sort | less |
| Saber si una variable está exportada | declare -p NOMBRE |
| Que los programas vean una variable | export NOMBRE=valor |
| Darle un valor a un solo comando | NOMBRE=valor comando |
| Ver un mensaje de error en inglés | LC_ALL=C comando |
Ver PATH con un directorio por línea | echo "$PATH" | tr ':' '\n' |
Agregar un directorio a PATH | PATH="$PATH:/ruta/del/directorio" |
| Saber qué archivo se ejecuta al escribir un comando | type -a comando |
| Elegir el editor por defecto | export EDITOR=nano, en ~/.bashrc |
Aplicar los cambios hechos en .bashrc | source ~/.bashrc |
Volver al .bashrc original de la distribución | cp /etc/skel/.bashrc ~/.bashrc |
| Abrir una shell sin mi configuración | bash --norc |
| Probar un indicador | PS1='\A \w \$ ' |
| Síntoma | Causa | Solució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 escribe | Qué hace |
|---|---|
nombre=valor | Crea o modifica una variable de la shell. |
$nombre, ${nombre} | Se reemplaza por su valor. |
unset nombre | La elimina. |
export NOMBRE=valor | La pone en el entorno: la heredan los programas. |
env, printenv | Muestran el entorno. |
NOMBRE=valor comando | La define solo para ese comando. |
PATH="$PATH:directorio" | Agrega un directorio a la búsqueda de comandos. |
source archivo, . archivo | Ejecuta un archivo en la shell actual. |
~/.profile | Se lee al iniciar la sesión: entorno y PATH. |
~/.bashrc | Se 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ón | Activan 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
- Una variable se crea con
nombre=valor, sin espacios, y se usa con$nombre. Una variable inexistente vale una cadena vacía. - El entorno es el conjunto de variables que un proceso recibe al nacer, como copia de las de su padre. Se lo ve con
env. - Una variable recién creada es local a la shell.
exportla incluye en el entorno, y desde entonces la heredan los programas. - La herencia va de padre a hijo: un proceso no puede modificar las variables de su padre. Por eso un script no cambia la shell que lo lanzó.
source archivoejecuta un archivo en la shell actual, y sus cambios quedan.VAR=valor comandodefine una variable solo para ese comando:LC_ALL=C sort.HOME,USER,PATH,LANG,EDITORyTERMson las variables de entorno que más influyen en el uso diario.PATH="$HOME/bin:$PATH"agrega un directorio a la búsqueda de comandos. Olvidar$PATHdeja la shell sin comandos.~/.profilese lee una vez, al iniciar la sesión, y es el lugar del entorno y dePATH.~/.bashrcse lee en cada terminal, y es el lugar de los alias, las funciones y el indicador..bashrcse edita con una copia de respaldo, agregando al final, y probando consourcey con una terminal nueva.- Una función agrupa comandos bajo un nombre y recibe argumentos en
$1,$2; resuelve lo que un alias no puede. PS1define el indicador, con códigos como\u,\h,\wy\$. Los colores se escriben entre\[y\].set -oyshopt -sactivan opciones de la shell, comonoclobberoglobstar.
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
- Crea una variable
cursocon el valorTerminal de Linux. Muéstrala dentro de una frase, entre comillas dobles y entre comillas simples. Elimínala y vuelve a mostrarla. - Cuenta cuántas variables de entorno tienes. Lista las que empiezan con
LC_oLANG, y las que contienen tu nombre de usuario en su valor. - Repite la experiencia de la sección 3 con una variable propia: comprueba con
bash -cque no se hereda, expórtala, comprueba que sí, y verifica su estado condeclare -pantes y después. - Ejecuta
ls -l,LC_ALL=C ls -lydate,LC_ALL=C date. ¿Qué cambia? Prueba tambiénTZ=Europe/Madrid date. - Muestra
PATHcon un directorio por línea y numerado. Averigua contype -aen cuál estápython3, y cuántos de esos directorios existen realmente. - Crea
~/biny, dentro, un programaahoraque muestre la fecha y la hora con el formato que prefieras. Agrégalo aPATHy ejecútalo desde/tmp. Luego abre una terminal nueva: ¿sigue funcionando? ¿Por qué? - Haz una copia de
~/.bashrcy recórrelo conless. Localiza la definición del historial, la dePS1y la línea que lee.bash_aliases. - Siguiendo los cuatro pasos de la sección 9, agrega a
.bashrctu editor preferido, un historial de 5000 líneas con fecha y la funciónmkcd. Compruébalo en una terminal nueva conecho $EDITOR,history | tail -n 3ytype mkcd. - 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. - Activa
noclobbere intenta sobrescribir un archivo de/tmpcon>, con>>y con>|. Luego pruebashopt -s autocdyshopt -s cdspell, y decide cuáles dejarías activadas.