Tema 105: Shells y scripts
Objetivos del tema 105
105.1 Personalizar y usar el entorno de shell
105.2 Personalización y escritura de scripts sencillos
105.1 Personalizar y usar el entorno de shell
Importancia
4
Descripción
El candidato debe ser capaz de personalizar el entorno de shell para adaptarlo a las necesidades de los usuarios así como de modificar perfiles globales y de usuario.
Áreas de conocimiento clave:
Establecer variables de entorno (e.g. PATH) al inicio de sesión o al generar un nuevo shell.
Escribir funciones en Bash para secuencias de comandos usadas con frecuencia.
Mantener el esqueleto de directorios para nuevas cuentas de usuario.
Establecer el directorio adecuado en la ruta de búsqueda de comandos.
Contenidos
El shell es posiblemente la herramienta más poderosa en un sistema operativo Linux y puede definirse como una interfaz entre usuario y kernel, tiene la función de Interpretar los comandos introducidos por el usuario, por lo tanto, los administradores de sistemas deben ser hábiles en su uso.
El Bourne Again Shell (Bash) es el shell de facto de la gran mayoría de las distribuciones de Linux. En el momento que el sistema operativo inicia, lo primero que el Bash (o cualquier otro shell) realiza, es ejecutar una serie de scripts de inicio que personalizan el entorno de sesión.
Existen varios scripts para todo el sistema operativo, así como también para usuarios específicos, en estos scripts podemos elegir las preferencias o configuraciones que mejor se adapten a las necesidades de nuestros usuarios en forma de variables, alias y funciones y la serie exacta de estos depende de un parámetro muy importante: El tipo de shell.
Iniciando una terminal
En un entorno de escritorio, podemos abrir una terminal o cambiar a una de las consolas del sistema fácilmente. Aquí tenemos que un nuevo shell es un pts
cuando se abre desde un emulador de terminal en el GUI o una tty
cuando se ejecuta desde una consola de sistema.
Como parte de las sesiones gráficas, los emuladores de terminales como gnome-terminal o konsole son muy amplios en características y fáciles de usar en comparación con las terminales de interfaz de usuario basadas en texto. A continuación, tienes algunos otros para hacerte una idea:
Usando las teclas Ctrl+Alt+F1-F6
podemos ir a los inicios de sesión de la consola que abren un shell de inicio de sesión interactivo basado en texto y la combinación de Ctrl+Alt+F7
llevará la sesión de vuelta al escritorio.
Después de iniciar sesión, escribe bash
en una terminal para abrir un nuevo shell (técnicamente, este shell es un proceso hijo del shell actual). Al iniciar el proceso hijo de bash, podemos especificar varias opciones para definir qué tipo de shell queremos iniciar. Aquí hay algunas importantes a la hora invocarlo:
bash -l
obash --login
-> Invocará un shell de inicio de sesión.bash -i
-> Invocará un shell interactivo.bash --noprofile
-> Con shell de inicio de sesión ignorará tanto el archivo de inicio de todo el sistema/etc/profile
como los archivos de inicio a nivel de usuario~/.bash_profile
,~/.bash_login
y~/.profile
.bash --norc
-> Con shell interactivo ignorará tanto el archivo de inicio del sistema/etc/bash.bashrc
como el archivo de inicio a nivel de usuario~/.bashrc
.bash --rcfile
-> Con shell interactivo tomará como el archivo de inicio ignorando a nivel de sistemaetc/bash.bashrc
y a nivel de usuario~/.bashrc
.
Ejecutando shells con su y sudo
A través del uso de estos dos programas (similares) podemos obtener tipos específicos de shells:
su
cambia el ID de usuario o lo convierte en superusuario (root). Con este comando podemos invocar ambos shells, el de con inicio de sesión y sin inicio de sesión:
su - user2
,su -l user2
osu --login user2
iniciará un shell de inicio de sesión interactivo como user2.su user2
iniciará un shell interactivo y sin inicio de sesión como user2.su - root
osu -
iniciará un shell de inicio de sesión interactivo como root.su root
osu
iniciará un shell interactivo y sin inicio de sesión como root.
sudo
por su lado, ejecuta comandos como otro usuario (incluyendo tambien el superusuario). Debido a que este comando se usa principalmente para obtener privilegios temporales de root, el usuario que lo use debe estar en el archivo ~/sudoers
(aunque por cuestiones de seguridad no es recomendable). Para añadir usuarios a ~/sudoers
necesitamos convertirnos en root y luego ejecutar:
Así como su, sudo nos permite invocar tanto los shells de inicio de sesión como los de no de inicio de sesión:
sudo su - user2
,sudo su -l user2
osudo su --login user2
iniciará un shell de inicio de sesión interactivo como user2.sudo su user2
osudo -u user2 -s
iniciará un shell interactivo sin inicio de sesión como user2.sudo su - root
orsudo su -
iniciará un shell de inicio de sesión interactivo como root.sudo -i
iniciará un shell de inicio de sesión interactivo como root.sudo -i <some_command>
iniciará un shell de inicio de sesión interactivo como root, ejecuta el comando y volverá al usuario original.sudo su root
orsudo su
iniciará un shell interactivo sin inicio de sesión como root.sudo -s
orsudo -u root -s
iniciará un shell sin inicio de sesión como root.
Cuando se usa su
o sudo
, es importante considerar el inicio de un nuevo shell y preguntarnos: ¿Necesitamos el entorno del usuario o no? Si es así, usaríamos las opciones que invocan las shells de inicio de sesión; si no, las que invocan sin inicio de sesión.
Para saber en qué tipo de shell estamos trabajando, podemos escribir:
También, para observar cuántos bash shells tenemos en ejecución en el sistema, podemos usar el comando:
Esto significa que el
user2
ha entrado en una sesión de GUI (Sistema de Ventanas X o en ingles: X Window System) y ha abierto gnome-terminal, luego ha pulsadoCtrl+Alt+F1
para entrar en una sesión terminal con inicio de sesión interactivo detty
(Mira como el último campo de cada línea esbash
para el primero y-bash
para el segundo). Finalmente, ha vuelto a la sesión del GUI presionandoCtrl+Alt+F7
y ha escrito el comandops aux | grep bash
. De esta manera, la salida muestra un shell interactivo sin inicio de sesión a través del emulador de terminal (pts/0
) y un shell de inicio de sesión interactivo a través de la propia terminal basada en texto (tty1
).
Shell interactivo de inicio de sesión
Primero de todo, para este shell, mencionar que los scripts de todo el sistema o globales se colocan en el directorio /etc/
, mientras que los locales o de nivel de usuario (local) se encuentran en el directorio /home
del usuario (~
):
Nivel Global
/etc/profile
-> el archivo .profile usado para todos los shell Bourne y shells compatibles (Incluido bash). A través de una serie de declaraciones if este archivo establece un número de variables como$PATH
y$PS1
, así como origen tanto del archivo/etc/bash.bashrc
como los del directorio/etc/profile.d
/etc/profile.d/*
-> Directorio que puede contener scripts que son ejecutados por/etc/profile
.
Nivel Local (~)
~/.bash_profile
-> archivo específico de Bash utilizado para configurar el entorno del usuario. También puede ser usado para crear el~/.bash_login
y~/.profile
~/.bash_login
-> este archivo (específicamente), sólo se ejecutará si no hay un archivo~/.bash_profile
.~/.profile
-> Este archivo no es específico de Bash y se obtiene sólo si no existen ni~/.bash_profile
ni~/.bash_login
, que es lo que normalmente ocurre. Por lo tanto, el propósito principal de este es el de revisar si se está ejecutando un shell de Bash, y si fuese afirmativo, obtener~/.bashrc
(si existe). Normalmente establece la variable$PATH
para que incluya el directorio privado del usuario~/bin
(si existe).~/.bash_logout
-> Si existe, este archivo específico de Bash hace algunas operaciones de limpieza al salir del shell, lo cual puede ser conveniente en casos como los de las sesiones remotas.
Para probar a mostrar algunos de estos archivos en acción, vamos a modificar /etc/profile
y /home/user2/.profile
. Añadiremos a cada uno una línea que nos recuerde el archivo que se está ejecutando:
Para probarlo, veamos qué pasa cuando user2 se conecta vía ssh desde otra máquina:
Como se observan en las dos últimas líneas, funcionó y nótese tres cosas:
El archivo global se ejecutó primero.
No había archivos
.bash_profile
o.bash_login
en el directorio "home" de user2.La tilde (
~
) se expandió a la ruta absoluta del archivo (/home/user2/.profile
).
Shell Interactivo sin inicio de sesión
Nivel Global
/etc/bash.bashrc
-> Este es el archivo.bashrc
de todo el sistema para los shells interactivos bash. A través de su ejecución, bash se asegura de que se está ejecutando interactivamente, comprueba el tamaño de la ventana después de cada comando (actualizando los valores de LÍNEAS y COLUMNAS, si es necesario) y establece algunas variables.
Nivel Local
~/.bashrc
-> Además de llevar a cabo tareas similares a las descritas para/etc/bash.bashrc
a nivel de usuario (como comprobar el tamaño de la ventana o si se está ejecutando de forma interactiva), este archivo específico de Bash suele establecer algunas variables de historial y origen~/.bash_aliases
(si existe). Aparte de eso, este archivo se utiliza normalmente para almacenaralias
y funciones específicas de los usuarios. Asimismo, también vale la pena señalar que~/.bashrc
se lee si bash detecta que su es una conexión de red (como ssh).
Modifiquemos ahora /etc/bash.bashrc
y /home/user2/.bashrc
:
Shell no Interactivo de inicio de sesión
Un shell no interactivo con las opciones -l o --login es forzado a comportarse como un shell de inicio de sesión y así los archivos de inicio a ser ejecutados serán los mismos que los de los shells de inicio de sesión interactivos.
Para probarlo, escribamos un simple script y hagámoslo ejecutable. No incluiremos ningún shebangs porque invocaremos el ejecutable bash (/bin/bash
con la opción de inicio de sesión) desde la línea de comandos:
Creamos el script
test.sh
para probar que el script se ejecuta con éxito:echo "echo 'Hello from a script'" > test.sh
Hacemos que nuestro script sea ejecutable:
chmod +x ./test.sh
Finalmente, invocamos a bash con la opción
l
para ejecutar el script:bash -l ./test.sh
Esto da como resultado:
¡Funciona! Antes de ejecutar el script, el login tuvo lugar y tanto el /etc/profile
como el ~/.profile
fueron ejecutados. Lo mismo ocurriria con una sesión de ssh.
Archivos fuentes
En las secciones anteriores hemos discutido que algunos scripts de inicio incluyen o ejecutan otros scripts, a este mecanismo se le llama "sourcing" y se explica en esta sección.
El punto (.
) se encuentra normalmente en los archivos de inicio, en el archivo .profile
de nuestro servidor de Debian podemos encontrar un ejemplo en el siguiente bloque:
Hemos observado cómo la ejecución de un script puede llevar a la de otro. Así la declaración if
garantiza que el archivo $HOME/.bashrc
— si existe (-f
) — se obtendrá (es decir, se leerá y se ejecutará) en el inicio de sesión.
Además, podemos usar el . siempre que hayamos modificado un archivo de inicio y queramos hacer efectivos los cambios sin necesidad de reiniciar. Por ejemplo, vamos a agregar un alias a /.bashrc
:
SKEL
SKEL
es por definición, el origen de los archivos de inicio de Shell, una variable cuyo valor es la ruta absoluta al directorio skel
el cual, sirve como plantilla para la estructura del sistema de archivos de los principales directorios de los usuarios. Incluye los archivos que serán heredados por cualquier nueva cuenta de usuario que se cree (incluyendo, por supuesto, los archivos de configuración de los shells). El SKEL
y otras variables relacionadas se almacenan en el /etc/adduser.conf
, que es el archivo de configuración para adduser:
Una buena práctica es crear un directorio en /etc/skel
para que todos los nuevos usuarios almacenen sus scripts personales:
Variables
Las variables son como una caja imaginaria en la que coloca temporalmente una información y al igual que los scripts de inicialización, Bash clasifica las variables como shell/local
(las que se ubican sólo dentro de los límites del shell en el que fueron creadas) o entorno/global
(las que son heredadas por shells y/o procesos hijos).
En Bash, dar un valor a un nombre se llama asignación de variables y es la forma en que creamos o establecemos las variables. Por otro lado, el proceso de acceder al valor contenido en el nombre se llama variable referenciada.
La sintaxis para la asignación de variables es: <variable_name>=<variable_value>
. Ten en cuenta de no dejar espacios a ninguno de los lados, por ejemplo:
El nombre de una variable puede contener letras (a-z,A-Z), números (0-9) y guiones bajos (_) pero no puede empezar con un numero ni puede contener espacios (ni siquiera usando comillas).
Para los valores también hay una serie de normas a seguir, estas pueden contener cualquier carácter alfanumérico (a-z,A-Z,0-9
) así como la mayoría de los caracteres (?,!,*,.,/, etc.
) pero si deseas poner un espacio, deberá ir entre comillas (" "
o ' '
) lo mismo ocurre si queremos poner símbolos como los utilizados para la redirección (<,>
) o el símbolo de "pipe" (|
).
Sin embargo, las comillas simples (' ') y dobles (" ") no siempre son intercambiables ya que según lo que hagamos con una variable (asignación o referencia), el uso de una u otra tiene implicaciones y dará resultados diferentes.
Las comillas simples toman todos los caracteres del valor de la variable LITERALMENTE.
Las comillas dobles permiten la sustitución de la variable por su valor.
Esto se ve muy bien con el siguiente ejemplo:
Variables locales o de Shell
Las variables locales o de shell existen sólo en el shell en el que se crean (De hecho, por convención, las estas las escribiremos en minúsculas). Es el caso de las variables que hemos visto en la sección anterior. Estas — siendo una variable local — no serán heredadas por ningún proceso hijo generado desde el shell actual.
En ciertos scripts la inmutabilidad puede ser una característica interesante de las variables. Para ello, podemos crearlas en modo sólo lectura (readonly):
Un comando útil cuando se trata de variables locales es set
. Este da salida a todas las variables y funciones de shell que se encuentran actualmente asignadas. Dado que pueden ser muchas líneas (¡pruébalo tú mismo!), se recomienda usarlo en combinación con un buscador como set | less
De la misma forma, para eliminar cualquier variable (ya sea local o global), usamos el comando unset
seguido del nombre de la variable sin el $
Variables globales o de entorno
Existen variables globales o de entorno para el shell actual, así como para todos los procesos subsecuentes que se derivan de este (Por convención, las variables de entorno se escriben en mayúsculas) es el caso de $SHELL
o de $PATH
.
Para que una variable de shell local se convierta en una variable de entorno, se debe utilizar el comando export <nombre>
(o tambien declare -x
), de esta formas convertimos nuestra variable local en una variable de entorno para que los shells hijos puedan reconocerla y usarla. Para volverla local otra vez simplemente usamos export -n <nombre>
.
El comando export
por si mismo mostrará un listado de las variables globales aunque también podemos listarlo con export -p
, env
y printenv
.
Es hora de revisar algunas de las variables de entorno más relevantes que se establecen en los archivos de configuración de Bash:
DISPLAY
-> En relación con el servidor X. Un valor vacío (:0
) para esta variable significa un servidor sin un sistema X WIndow, mientras que un número extra (como enmi.xserver:0:1
) se referiría al número de pantalla (si existe más de uno).HISTFILE
-> El nombre del archivo que almacena todos los comandos a medida que se escriben. Por defecto este archivo se encuentra en~/.bash_history
y podemos verlo con el comandohistory
.HISTCONTROL
-> Esta variable controla qué comandos se guardan enHISTFILE
. Hay tres valores posibles:ignorespace
Los comandos que empiecen con un espacio no se guardarán.ignoredups
Un comando que es el mismo que el anterior no se guardará.ignoreboth
Los comandos que caen en cualquiera de las dos categorías anteriores no se guardarán.
HISTSIZE
-> Establece el número de comandos que se almacenarán en la memoria mientras dure la sesión de shell.HISTFILESIZE
-> Establece el número de comandos que se guardarán enHISTFILE
tanto al principio como al final de la sesión.HOME
-> La variable almacena la ruta absoluta del directorio principal del usuario y se establece cuando el usuario se conecta. Recuerda que~
es equivalente a$HOME
HOSTNAME
-> Variable que almacena nombre del computador en la red.HOSTTYPE
-> Almacena la arquitectura de la unidad central de procesamiento (CPU) del computador.LANG
-> Esta variable guarda la información de localización que utiliza el sistema.MAIL
-> Esta variable almacena el archivo en el que Bash revisa el correo electrónico.MAILCHECK
-> Esta variable almacena un valor numérico que indica en segundos la frecuencia con la que Bash comprueba si hay correo nuevo.PATH
-> Esta almacena la lista de directorios donde Bash busca los archivos ejecutables cuando se le indica que ejecute cualquier programa, en la mayoría de casos, esta variable se establece a través del archivo/etc/profile
de todo el sistema. Si quisiéramos incluir la carpeta/usr/local/sbin
en el$PATH
para usuarios habituales, modificaremos la línea de ese archivo:
También podríamos hacerlo desde la linea de comandos así:
PS1
-> Esta variable almacena el valor del indicador Bash, también de/etc/profile
, la sentenciaif
comprueba la identidad del usuario y en consecuencia brinda un indicador muy discreto (#
para root o$
para usuarios regulares):
SHELL
-> Almacena la ruta absoluta del shell actual.USER
-> Almacena el nombre del usuario actual.
Ejecución de programa en entorno modificado
El comando env
también puede ser usado para modificar el entorno del shell en el momento de la ejecución de un programa, si quisieramos abrir un shell lo más vacio posible de variables escribiriamos:
En este mismo topic, mas arriba, cuando hablamos de los shells no interactivos sin inicio de sesión, dijimos como los scripts no leen ningún archivo de inicio estándar sino que buscan el valor de la variable $BASH_ENV
y lo usan como su archivo de inicio si existe, vamos a comprobarlo:
Recuerda que usamos
./test_env.sh
para ejecutar el script desde su propio directorio.
Creando Alias
Un alias es un nombre sustituto de otro(s) comando(s)y puede ejecutarse como un comando normal, pero en su lugar ejecuta otro comando según la definición de alias. Su sintaxis es muy sencilla:
Entre algunas de las cosas que se pueden hacer incluye asignar y referenciar variables dentro de la declaración del alias, generar alias con nombres de comandos ya existentes (siempre tendrán prioridad los alias aunque puedes "escapar" de ellos escribiendo \
delante) o inlcuso poner un alias dentro de un alias.
Al igual que con las variables, para que nuestros alias ganen persistencia, debemos escribirlos en scripts de inicialización que se ejecuten al inicio como es ~/.bashrc
. Probablemente encontrarás algunos alias allí (la mayoría de ellos comentados y listos para ser usados eliminando el # principal) e incluso como se puede leer en las últimas tres líneas, se nos ofrece la posibilidad de tener nuestro propio archivo dedicado a los alias ( ~/.bash_aliases
) y ser ejecutado por .bashrc
con cada inicio del sistema así que podemos ir por esa opción, crear y editar dicho archivo para añadir los alias necesarios.
Después de añadir alias o funciones a cualquier archivo de script de inicio, debe ejecutar tales archivos con .
o source
para que los cambios surtan efecto si no quiere salir y volver a entrar o reiniciar el sistema.
Creando funciones
En comparación con los alias, las funciones son más programables y flexibles, especialmente cuando se trata de explotar todo el potencial de las variables incorporadas y los parámetros posicionales de Bash. Se trata de comandos que incluye la lógica a través de bloques o colecciones de otros comandos.
Hay dos sintaxis válidas para definir las funciones:
Es común agregar funciones en archivos o scripts pero, también se puede escribir con cada comando en el shell prompt, en una línea diferente. Si decidimos saltarnos los ENTER
y escribir una función en una sola línea, los comandos deben estar separados por punto y coma (también el último comando):
Ahora, para invocarla, tan solo debemos escribir su nombre en la terminal.
Bash trae un conjunto de variables especiales que son particularmente útiles para funciones y scripts, estas son especiales porque sólo pueden ser referenciadas — no asignadas:
$?
-> Esta variable es usada para referenciar si el último comando usado ha dado error o no, un valor de 0 significa éxito y un valor distinto significara error.$$
-> Muestra el PID del shell.$!
-> Muestra el PID del ultimo comando ejecutado de fondo (por defecto se ejecuta con&
al final para que se hagan de fondo).$#
-> Muestra cuantos argumentos que se le pasan al comando.$@, $*
-> Se extienden a los argumentos pasados al comando.$_
-> Se expande hasta el último parámetro o el nombre del script (¡revisa "man bash" para conocer más!).$0 a $9
-> Parámetros posicionales, se usan para hacer una especie de matriz unitaria o como parámetros de orden y llamar a un listado siendo $0 siempre la del shell.
De esta forma se pueden pasar parámetros para usarlos dentro de la función:
Como se ha dicho antes, podemos agregar una función dentro de un alias:
Vamos a desglosarlo:
Primero está la función en sí misma que, desglosada sería así:
El último comando de la función (
unset -f gr8_ed
) borra la función para que no permanezca en la sesión actual de bash después de que el alias sea llamado.Por último, invocamos la propia función:
gr8_ed
.
105.2 Personalización y escritura de scripts sencillos
Importancia
4
Descripción
El candidato debe ser capaz de personalizar scripts existentes o de escribir nuevos scripts sencillos en Bash.
Áreas de conocimiento clave:
Usar la sintaxis estándar sh (bucles, tests).
Usar la sustitución de comandos.
Evaluar correctamente el código de retorno de un comando en caso de éxito, fracaso o cualquier otra información que proporcione la salida del comando.
Ejecutar comandos en cadena.
Realizar envío de correo condicional al superusuario.
Seleccionar correctamente el intérprete del script mediante la línea inicial o shebang (#!).
Gestionar la ubicación, los propietarios, la ejecución y los permisos suid de los scripts.
Contenidos
Last updated