# 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*

<table data-header-hidden><thead><tr><th width="148"></th><th></th></tr></thead><tbody><tr><td><strong>Importancia</strong></td><td>4</td></tr><tr><td><strong>Descripción</strong></td><td>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.</td></tr></tbody></table>

#### *Á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.&#x20;

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.&#x20;

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.&#x20;

### Iniciando una terminal&#x20;

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.&#x20;

{% hint style="info" %}
`tty` significa "*teletypewritter*"; `pts` significa "pseudo terminal slave". Para más información: `man tty` y man pts.
{% endhint %}

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:

<figure><img src="https://727141126-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGDxv9DabSo9OMw53A4vS%2Fuploads%2FvCSCMZHPiZ1wdd1doO7H%2Fimage.png?alt=media&#x26;token=7145c2e0-abb2-41f5-8b34-174911f7b5f2" alt="" width="375"><figcaption><p>Fuente: @dan_nanni</p></figcaption></figure>

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.&#x20;

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:&#x20;

* `bash -l` o `bash --login` -> Invocará un shell de inicio de sesión.&#x20;
* `bash -i` -> Invocará un shell interactivo.&#x20;
* `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`.&#x20;
* `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`.&#x20;
* `bash --rcfile` -> Con shell interactivo tomará como el archivo de inicio ignorando a nivel de sistema `etc/bash.bashrc` y a nivel de usuario `~/.bashrc`.&#x20;

### Ejecutando shells con su y sudo&#x20;

A través del uso de estos dos programas (similares) podemos obtener tipos específicos de shells:&#x20;

`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:&#x20;

* `su - user2`, `su -l user2` o `su --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.&#x20;
* `su - root` o `su -` iniciará un shell de inicio de sesión interactivo como root.&#x20;
* `su root` o `su` iniciará un shell interactivo y sin inicio de sesión como root.&#x20;

`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:

```bash
sudo su
usermod -aG sudo user2 
```

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` o `sudo su --login user2` iniciará un shell de inicio de sesión interactivo como user2.
* `sudo su user2` o `sudo -u user2 -s` iniciará un shell interactivo sin inicio de sesión como user2.&#x20;
* `sudo su - root` or `sudo su -` iniciará un shell de inicio de sesión interactivo como root.&#x20;
* `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.&#x20;
* `sudo su root` or `sudo su` iniciará un shell interactivo sin inicio de sesión como root.&#x20;
* `sudo -s` or `sudo -u root -s` iniciará un shell sin inicio de sesión como  root.

{% hint style="warning" %}
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.
{% endhint %}

Para saber en qué tipo de shell estamos trabajando, podemos escribir:

```bash
 echo $0 
#Y dependiendo de la salida:
-bash o -su        #Inicio de sesión interactivo 
bash o /bin/bash   #Sin inicio de sesión interactivo
<name_of_script>   #Sin inicio de sesión no interactivo (scripts)  
```

&#x20;También, para observar cuántos bash shells tenemos en ejecución en el sistema, podemos usar el comando:

```bash
ps aux | grep bash
#Podriamos ver algo así:
# user2 5270 0.1 0.1 25532 5664 pts/0 Ss 23:03 0:00 bash
# user2 5411 0.3 0.1 25608 5268 tty1 S+ 23:03 0:00 -bash
# user2 5452 0.0 0.0 16760  940 pts/0 S+ 23:04 0:00 grep --color=auto bash
```

* 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 pulsado `Ctrl+Alt+F1` para entrar en una sesión terminal con inicio de sesión interactivo de`tty` (Mira como el último campo de cada línea es `bash` para el primero y `-bash` para el segundo). Finalmente, ha vuelto a la sesión del GUI presionando `Ctrl+Alt+F7` y ha escrito el comando `ps 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`).&#x20;

### 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&#x20;

* `/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`.&#x20;

#### 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`.&#x20;
* `~/.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).&#x20;
* `~/.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:&#x20;

```bash
echo 'echo Hello from /etc/profile' >> /etc/profile 
echo 'echo Hello from ~/.profile' >> ~/.profile
```

Para probarlo, veamos qué pasa cuando user2 se conecta vía ssh desde otra máquina:&#x20;

```bash
ssh user2@192.168.1.6
#Linux debian 4.9.0-8-amd64 #1 SMP Debian 4.9.130-2 (2018-10-27) x86_64

#Los programas incluidos en el sistema Debian GNU/Linux son software libre; los términos
#exactos de distribución de cada programa se describen en archivos individuales en
#/usr/share/doc/*/copyright.

#Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
#permitted by applicable law.
#Last login: Tue Nov 27 19:57:19 2018 from 192.168.1.10
#Hello from /etc/profile
#Hello from /home/user2/.profile
```

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.&#x20;
* La tilde (`~`) se expandió a la ruta absoluta del archivo (`/home/user2/.profile`).

### Shell Interactivo sin inicio de sesión&#x20;

#### Nivel Global&#x20;

* `/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.&#x20;

Nivel Local&#x20;

* `~/.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 almacenar `alias` 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`:&#x20;

```bash
echo 'echo Hello from /etc/bash.bashrc' >> /etc/bash.bashrc
echo 'echo Hello from ~/.bashrc' >> /.bashrc 

#Esto ocurrirar cuando user2 comienza un nuevo shell: 
bash 
# Hello from /etc/bash.bashrc
# Hello from /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.&#x20;

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:

1. Creamos el script `test.sh` para probar que el script se ejecuta con éxito: `echo "echo 'Hello from a script'" > test.sh`
2. Hacemos que nuestro script sea ejecutable:  `chmod +x ./test.sh`
3. Finalmente, invocamos a bash con la opción `l` para ejecutar el script: `bash -l ./test.sh`&#x20;
4. Esto da como resultado:&#x20;

```
#Hello from /etc/profile
#Hello from /home/user2/.profile
#Hello from a script
```

¡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.&#x20;

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:

```bash
# include .bashrc if it exists
  if [ -f "$HOME/.bashrc" ]; then
  . "$HOME/.bashrc"
  fi
```

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.

{% hint style="info" %}
El tema de alias y variables lo veremos más a fondo en el siguiente [topic 105.2](#id-105.2-personalizacion-y-escritura-de-scripts-sencillos)
{% endhint %}

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`:&#x20;

```bash
echo "alias hi='echo We salute you.'" >> ~/.bashrc

# Ahora, imprime la última línea de /.bashrc para comprobar que todo ha ido bien:
tail -n 1 !$ #Para ssh
tail -n 1 ~/.bashrc 
# alias hi='echo We salute you.' 

#Se ejecuta el archivo manuamente:
. /.bashrc

#Y vemos que funciona:
hi
# hi We salute you.
```

{% hint style="info" %}
El comando `source` es un sinónimo de (`.`) así que para ejecutar `/.bashrc` también podemos hacerlo de esta manera:&#x20;

```bash
source ~/.bashrc
```

{% endhint %}

#### SKEL&#x20;

`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*:

```bash
grep SKEL /etc/adduser.conf
# La variable SKEL especifica el directorio que contiene el usuario "skeletal"...
SKEL=/etc/skel
# Si SKEL_IGNORE_REGEX está configurado, adduser ignorará los archivos que coincidan con este.
SKEL_IGNORE_REGEX="dpkg-(old|new|dist|save)"

#Lo encontraremos aquí y si hacemos un -a veremos los scripts de inicio de shells:
ls -a /etc/skel/
```

Una buena práctica es crear un directorio en `/etc/skel` para que todos los nuevos usuarios almacenen sus scripts personales:

```bash
#1- Como root nos movemos a /etc/skel:
sudo su 
cd /etc/skel/

#2- Listamos su contenido:
ls -a

#3- Creamos nuestro directorio y comprobamos que todo ha ido como se esperaba:
mkdir my_personal_scripts
ls -a

#4- Ahora borramos user2 junto con su directorio home:
deluser --remove-home user2

#5- Añadimos user2 de nuevo para que tenga un nuevo directorio principal:
adduser user2

#6- Finalmente, entramos como user2 y listamos todos los archivos en /home/user2 para comprobar el resultado:
su - user2
ls -a /home/user2
#Veremos: . .. .bash_history .bash_logout .bashrc my_personal_scripts .profile
```

### 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:&#x20;

```bash
distro=zorinos 

#Para comprobar su valor usamos echo y la variable con $:
echo $distro
#Resultado: zorinos
```

{% hint style="danger" %}
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).

<pre class="language-bash"><code class="lang-bash"><strong>#SÍ:
</strong><strong>distro=zorinos
</strong>DISTRO=zorinos
distro_1=zorinos
_distro=zorinos
my_distro=zorinos

#Pero NO:
1distro=zorinos
"my distro"=zorinos
</code></pre>

{% endhint %}

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" (`|`).

```bash
#Esto SÍ
distro=zorin12.4?
distro="zorin 12.4"
distro='zorin 12.4'
distro=">zorin"
```

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.&#x20;

* 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:

```bash
lizard=uromastyx

animal='My $lizard'
echo $animal
#Resultado: My $lizard

animal="My $lizard"
echo $animal
#Resultado: My uromastyx
```

### 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*):&#x20;

```bash
readonly reptile=tortoise 

#O bien a posterior:
reptile=tortoise 
readonly reptile

#Esto dará error al volver a asignar la variable
#Para listar todas las variables de sólo lectura en nuestra sesión actual, podemos escribir:
readonly
#ó
readonly -p
```

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>` .&#x20;

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 en `mi.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 comando `history`.
* `HISTCONTROL` -> Esta variable controla qué comandos se guardan en `HISTFILE`. Hay tres valores posibles:&#x20;
  * `ignorespace` Los comandos que empiecen con un espacio no se guardarán.&#x20;
  * `ignoredups` Un comando que es el mismo que el anterior no se guardará.&#x20;
  * `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 en `HISTFILE` 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.
* `LD_LIBRARY_PATH` -> Esta variable consiste en un conjunto de directorios separados por dos puntos donde las bibliotecas compartidas (*shared libraries*) son compartidas por los programas, se habló más de esto en el [topic 102.3](https://apuntes-alex.gitbook.io/apuntes-lpic-1/examen-101/tema-102-instalacion-de-linux-y-gestion-de-paquetes#id-102.3-gestion-de-librerias-compartidas).
* `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:

```bash
if [ "`id -u`" -eq 0 ]; then
  PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
else
  PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/usr/local/sbin"
#Añadimos al final, separado por ":" 
fi
export PATH
```

También podríamos hacerlo desde la linea de comandos así:

```bash
PATH=/usr/local/sbin:$PATH
#ó bien si queremos añadirlo al final de la linea:
PATH=$PATH:/usr/local/sbin
```

* `PS1` -> Esta variable almacena el valor del indicador Bash, también de `/etc/profile`, la sentencia `if` comprueba la identidad del usuario y en consecuencia brinda un indicador muy discreto ( `#` para root o `$` para usuarios regulares):

```bash
if [ "`id -u`" -eq 0 ]; then
  PS1='# '
else
  PS1='$ '
fi
```

{% hint style="info" %}
RECUERDA: El id de `root` es 0. Conviértete en `root` y puedes comprobarlo tú mismo con `id -u`.
{% endhint %}

* `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:

```bash
env -i bash 
#Ahora la mayoría de las variables de nuestro entorno han desaparecido.
#Podemos comprobarlo:
echo $USER
env
#Devolverá solo unos pocos
```

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:

<pre class="language-bash"><code class="lang-bash">#1- Creamos nuestro propio archivo de inicio con el siguiente contenido:
cat .startup_script
    CROCODILIAN=caiman

#2- Escribimos un script Bash  con el siguiente contenido:
<strong>cat test_env.sh
</strong><strong>    #!/bin/bash
</strong><strong>    echo $CROCODILIAN
</strong><strong>    
</strong><strong>#3- Establecemos que el script test_env.sh sea ejecutable:
</strong>chmod +x test_env.sh

#4- Por último, usamos env para establecer la variable BASH_ENV en startup_script para test_env.sh:
env BASH_ENV=/home/user2/.startup_script ./test_env.sh
<strong>#lo cual nos devolverá "caiman"
</strong><strong>
</strong>#El comando env está implícito incluso si nos deshacemos de este:
BASH_ENV=/home/user2/.startup_script ./test_env.sh
</code></pre>

* Recuerda que usamos `./test_env.sh` para ejecutar el script desde su propio directorio.

### Creando Alias&#x20;

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:

{% code overflow="wrap" %}

```bash
# alias alias_name="command(s)"

# Por ejemplo:
alias oldshell=sh
#Este alias iniciará una instancia del shell original sh cuando el usuario digite oldshell en la terminal

#Otro ejemplo, alias nos permite "acortar" comandos:
alias ls='ls --color=auto'

#Tambien nos permite concatenar comandos con ";"
alias git_info='which git;git --version'

#Por si mismo mostrará un listado de alias:
alias
#Para borrarlos: 
unalias git-info


```

{% endcode %}

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.

{% hint style="success" %}
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.
{% endhint %}

### 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:&#x20;

{% code overflow="wrap" %}

```bash
#Usando la palabra clave function seguida del nombre de la función y los comandos entre corchetes
function function_name {
command #1
command #2
command #n
}

#O bien, podemos omitir la palabra function y usar dos paréntesis () justo después del nombre de la función:
function_name() {
command #1 
command #2 
command #n 
}
```

{% endcode %}

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):

```bash
greet() { greeting="Hello world!"; echo $greeting; }
```

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:&#x20;

* `$?` -> 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.&#x20;
* `$@, $*` -> Se extienden a los argumentos pasados al comando.&#x20;
* `$_` -> Se expande hasta el último parámetro o el nombre del script (<mark style="color:red;">¡revisa "man bash" para conocer más!</mark>).
* `$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.

```bash
#por ejemplo, creamos la siguiente funcion:
$ special_vars() {
> echo $0
> echo $1
> echo $2
> echo $3
}

#Ahora le pasamos parametros:
special_vars debian ubuntu zorin

#Devolverá:
# -bash
# debian
# ubuntu
# zorin

```

De esta forma se pueden pasar parámetros para usarlos dentro de la función:

<pre class="language-bash"><code class="lang-bash">#home/user2/funed.sh :

#!/bin/bash
<strong>editors() {
</strong><strong>
</strong>editor=emacs

echo "The text editor of $USER is: $editor."
echo "Bash is not a $1 shell."
}
#Ahora le pasamos el parámetro:
editors tortoise

#Ejecutamos:
./funed.sh
    #The text editor of user2 is: emacs.
    #Bash is not a tortoise shell.
    
#El parámetro lo podemos incluir en el script o pasarselo por shell
</code></pre>

<details>

<summary>Caso de ejemplo, script de información al usuario</summary>

Haciendo uso de la variable global `PS1` vamos a crear una función llamada `fyi` (que se colocará en un script de inicio) que le dará al usuario la siguiente información:&#x20;

* Nombre del usuario&#x20;
* Directorio principal
* Nombre del host
* Tipo de sistema operativo
* Buscar la ruta (PATH) de ejecutables
* Directorio de correo
* Con qué frecuencia se revisa el correo
* ¿Cuántos shells tiene la sesión actual?
* prompt (deberías modificarlo para que muestre @)&#x20;

Para lograr ese propósito, hemos puesto la función en `/home/user2/.bashrc`

<pre class="language-bash"><code class="lang-bash">fyi() {
<strong>    echo -e "For your Information:\n
</strong>    Username: $USER
    Home directory: $HOME
    Host: $HOSTNAME
    Operating System: $OSTYPE
<strong>    Path for executable files: $PATH
</strong>    Your mail directory is $MAIL and is searched every $MAILCHECK seconds.
    The current level of your shell is: $SHLVL"
    PS1="\u@\h-\d " 
} 

fyi
</code></pre>

</details>

Como se ha dicho antes, podemos agregar una función dentro de un alias:&#x20;

{% code fullWidth="true" %}

```bash
alias great_editor='gr8_ed() { echo $1 is a great text editor; unset -f gr8_ed; }; gr8_ed' 
```

{% endcode %}

Vamos a desglosarlo:&#x20;

* Primero está la función en sí misma que, desglosada sería así:

```bash
gr8_ed() {
echo $1 is a great text editor
unset -f gr8_ed
}
```

* 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`.

<details>

<summary>Caso de ejemplo, saludo y advertencia a usuario</summary>

Vamos a probar otro ejemplo, pongámonos en que queremos comunicar dos cosas a `user2` cada vez que se registre en el sistema que será, saludar y recomendar un editor de texto y advertirle sobre que la carpeta de videos esta añadiendo muchas cosas (`$HOME/Video`).

Para lograr ese propósito, hemos puesto las siguientes dos funciones en `/home/user2/.bashrc`.&#x20;

La primera función (check\_vids) hace el chequeo de los archivos .mkv y la advertencia:

{% code overflow="wrap" %}

```bash
check_vids() { 
    ls -1 ~/Video/*.mkv > /dev/null 2>&1 
    if [ "$?" = "0" ];then 
        echo -e "Recuerda, no deberias guardar mas de 5 vídeos en tu carpeta de vídeos.\nGracias." 
        else 
        echo -e "No tienes ningún video en tu carpeta. Puedes guardar hasta 5.\nGracias." 
        fi 
    }
```

{% endcode %}

Esto hace tres cosas:&#x20;

* Lista los archivos `.mkv` en `~/Video` enviando la salida (y cualquier error `2>&1`) al llamado *bit-bucket* (`/dev/null`).
* Prueba la salida del comando anterior para el éxito (recuerda, si `$?=0` es que ha tenido exito).
* Dependiendo del resultado de la prueba, imprime uno de los dos mensajes.

Ahora vamos con la segunda función que es una versión modificada del ejemplo de más arriba:

<pre class="language-bash"><code class="lang-bash">editors() {
    editor=emacs
<strong>    echo "Hola, $USER!"
</strong>    echo "$editor es más que un editor de texto!"
    check_vids
    }

editors
</code></pre>

Es importante observar dos cosas:

* El último comando de editors invoca `check_vids` para que ambas funciones se encadenen: El saludo, el elogio, el chequeo y la advertencia se ejecutan en secuencia.&#x20;
* `editors` es el mismo punto de entrada a la secuencia de funciones, por lo que se invoca en la última línea (`editors`).&#x20;

Ahora, entremos como `user2` y probemos que funciona:

```bash
su - user2
    #Hola, user2! emacs es más que un editor de texto! 
    #Recuerda, no deberias guardar mas de 5 vídeos en tu carpeta de vídeos.
    #Gracias.
```

</details>

***

## 10&#x35;**.2 Personalización y escritura de&#x20;*****scripts*****&#x20;sencillos**

<table data-header-hidden><thead><tr><th width="148"></th><th></th></tr></thead><tbody><tr><td><strong>Importancia</strong></td><td>4</td></tr><tr><td><strong>Descripción</strong></td><td>El candidato debe ser capaz de personalizar <em>scripts</em> existentes o de escribir nuevos <em>scripts</em> sencillos en Bash.</td></tr></tbody></table>

#### *Á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*
