[Lenguajes: BASH]
Problema
En Linux/BASH, en ocasiones es necesario modificar temporalmente alguna de las variables globales del sistema, y, a menudo esta operación se quiere hacer de manera automática empleando un programa de BASH, guardado en un archivo, que es lo que se conoce como un Script y que se invocará manualmente desde una terminal.
Para dar un poco más de contexto real al problema, supongamos que se desea cambiar una de las entradas en la variable global PATH, por otra, sin modificar ninguna otra de las entradas. Sea, por ejemplo, el siguiente el valor de la variable PATH:
# El valor de la variable PATH
echo $PATH
## /home/checo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
Supongamos que en esta variable se quiere cambiar solamente la entrada /usr/games
por una
semejante a /disco0/usr/games
, dejando intactas todas las demás entradas.
Aquí entonces el problema se subdivide en dos, a saber:
- ¿Cómo producimos, a partir de la variable PATH, el nuevo valor que debe tener ésta?
- ¿Cómo se establece ese resultado como el nuevo valor de la variable global PATH?
Solución
Calculando el nuevo valor que debe tener la variable PATH
Aquí proponemos dos estrategias para resolver este problema:
- Utilizar la substitución nativa de BASH para filtrar la variable.
- Emplear el editor de flujos de textos sed para filtrar la variable.
Empleo de la sustitución nativa de BASH
El formato para esta instrucción es ${ variable / que-sustituir / reemplazo }. Como tanto el que-sustituir, como el reemplazo, contienen, en este caso, la diagonal (/) que también forma parte de la instrucción, se tiene que distinguir en esos textos de entrada, justamente como texto y no como parte de la instrucción, mediante el prefijo del caracter backslash (\). De este modo, las instrucciones en BASH, para ejecutar la sustitución es como sigue:
PATH=${PATH/\/usr\/games/\/disco0\/usr\/games}
echo $PATH
## /home/checo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/disco0/usr/games:/usr/local/games
Empleo de sed
Típicamente, sed toma como entrada un archivo, que, en caso de omisión es el stdin
o standard input. Una variable común, que contenga texto, se puede convertir en el
stdin, mediante el operador de indirección <<<
. Por otra parte, el comando de sustitución
de sed, es parecido al nativo de BASH: “s*/ que-sustituir / reemplazo /*”.
La diferencia es que, en este caso, para evitar confusiones en el comando, el caracter de
separación no es necesariamente la diagonal, y puede ser cualquiera que no aparezca en
los textos de entrada. Así, para producir una salida como la que deseamos se podría hacer
así:
sed "s:/usr/games:/disco0/usr/games:" <<<$PATH
## /home/checo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/disco0/usr/games:/usr/local/games
Lo que hay que notar aquí, es que el resultado no se ha guardado en ninguna variable, sino que se ha
mandado directamente al stdout. Para guardar el resultado en una variable se hace mediante
la instrucción de expansión de comandos, $(comando )
. Así que, lo que queremos lograr se
hace con:
PATH=$(sed "s:/usr/games:/disco0/usr/games:" <<<$PATH)
echo $PATH
## /home/checo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/disco0/usr/games:/usr/local/games
Guardando el valor en la variable global PATH
En lo que se ha descrito anteriormente, las instrucciones se han dado en línea, directamente en una terminal. Sin embargo, la tarea se tiene que hacer mediante un archivo script que contenga las instrucciones.
Aparte, supuestamente la instrucción export
de BASH, sirve para modificar variables
globales. Así que el contenido de nuestro archivo al que llamaremos "cambia.sh", será el
que se muestra a continuación:
#! /bin/bash
PATH=$(sed "s:/usr/games:/disco0/usr/games:" <<<$PATH)
export PATH
echo $PATH
Intencionalmente hemos dejado la instrucción echo $PATH
en el script, para monitorear
el valor de la variable en el interior del script. Se ha hecho además que el script
sea ejecutable mediante la instrucción chmod +x cambia.sh
, de modo que se pueda
invocar su ejecución fácilmente. Veamos pues su comportamiento:
./cambia.sh
echo $PATH
## /home/checo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/disco0/usr/games:/usr/local/games
## /home/checo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
La primer impresión de la variable corresponde a la que se hizo en el interior del
script "cambia.sh", la segunda, que como se puede ver es diferente y no manifiesta el
cambio que se intentaba, corresponde al comando echo $PATH
que se ha ejecutado desde
la terminal. Esto indica que la variable gobal PATH no se ha cambiado.
Este resultado es producto de la forma como se ha invocado la ejecución de "cambia.sh".
En este caso, se ha creado un proceso independiente, si bien no separado, del que se está
ejecutando en la terminal desde donde lo hemos invocado. Para establecer su ejecución
dentro del mismo proceso, se hace con la instrucción source Script
, o bien . Script
.
La llamada queda como sigue:
source cambia.sh
echo $PATH
## /home/checo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/disco0/usr/games:/usr/local/games
## /home/checo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/disco0/usr/games:/usr/local/games
Finalmente, si queremos convertir esta llamada en un comando se puede hacer mediante
la instrucción alias
, que se puede poner en archivos de inicialización tales como
el “.bashrc”, así:
alias cambia='source /aqui-path-completo-hacia/cambia.sh'
Problema resuelto.