El caparazón de Bash

Una introducción del usuario al shell Bash

Bash es (a partir de hoy)la de facto shell on most systems you’ll get in touch with: Linux, macOS, and the WSL on Windows 10.

Actualización: macOS desde Catalina (otoño de 2019) usa Zsh

Hay razones históricas que hicieron de Bash el caparazón más popular del mundo. En 1989, cuando se lanzó por primera vez, el mundo de la tecnología era muy diferente. En ese momento, la mayor parte del software del mundo UNIX era de código cerrado. El propio Unix era propietario y de código cerrado.

Para usar un sistema UNIX, tenía que usar un shell.

El shell más popular en ese momento era de código cerrado y propietario, tenía que pagar para usarlo. Era el caparazón de Bourne, disponible bajo el/bin/shmando. Se llamó “Bourne” porque su creador fue Steve Bourne.

Richard Stallman en esos años con el Proyecto GNU (y más tarde con Linux) estaba a punto de revolucionarlo todo, iniciando la revolución del Código Abierto. El Proyecto GNU necesitaba un caparazón y, con la ayuda de la Free Software Foundation, nació Bash. Fuertemente inspirado en el Bourne Shell, Bash significaBourne Again Shelly es un ingrediente clave del Proyecto GNU, y probablemente una de sus piezas de software más exitosas que todavía usamos hoy.

Bash podría ejecutar todos los scripts escritos parash, que era una característica obligatoria para su adopción, y también introdujo muchas más características, desde los primeros días, ofreciendo una mejor experiencia a sus usuarios. Desde esos primeros días, Bash obtuvo muchas mejoras. Este tutorial describe las cosas más populares y útiles que puede hacer con él.

Primeros pasos en Bash

Dado que Bash es el shell predeterminado en muchos sistemas, todo lo que necesita para iniciar un shell bash es

  • inicie sesión en el sistema, si es un servidor
  • abre tu terminal, si es tu computadora

Mira miguía de terminal macOSpara obtener más información sobre el uso de su terminal en una Mac.

Tan pronto como lo inicie, debería ver un mensaje (que generalmente termina con$).

¿Cómo sabes que el shell está ejecutando bash? Intenta escribirhelpy presionando enter.

¿Ver? Le dijimos a Bash que ejecutara elhelpmando. Este comando, a su vez, le muestra la versión de Bash que está ejecutando y una lista de comandos que puede usar.

Advertencia: ¿ves la versión que tengo allí? Es 3.2.57. Esta es la versión de Bash predeterminada enviada en macOS, que no incluye una versión superior para problemas de licencia. Esta versión de Bash es de 2014. Instale la última versión de Bash 5.x usando Homebrew, escribiendobrew install bash.

La mayoría de las veces, nunca usará ninguno de los comandos enumerados en la ayuda de bash, a menos que esté creandoscripts de shello cosas avanzadas.

El 99% del uso diario de shell es navegar a través de carpetas y ejecutar programas comols,cdy otras utilidades comun de UNIX.

Para navegar por el sistema de archivos, utilizará ellsmando. Está disponible en/bin/ls, y dado que Bash tiene la/bincarpeta en su lista de rutas, puede escribirlspara usarlo.

lsenumera los archivos en la carpeta actual. Por lo general, comienza desde su carpeta de inicio, que depende del sistema, pero en macOS se encuentra en/Users. Mi carpeta de inicio está en/Users/flavio. Esto no está relacionado con Bash, es más una cosa del sistema de archivos UNIX, pero los argumentos se superponen y si nunca ha usado un shell, es bueno saberlo.

Para navegar a otras carpetas, usa elcdcomando, seguido del nombre de la carpeta a la que desea mover:

cd Documents

cd ..vuelve a la carpeta principal.

Dependiendo de su configuración de Bash, verá su carpeta actual mostrada antes del indicador (el$símbolo). O puede que no, pero siempre puede saber dónde se encuentra escribiendopwdy presionando enter.

pwdsignifica * p * atname de * w * orking * d * irectory

Edición de línea de comando

Cuando escriba sus comandos en el shell, observe que puede moverse hacia la izquierda y hacia la derecha con las teclas de flecha. Esta es una característica de shell. Puede moverse por sus comandos, presionar el botón de retroceso y corregir los comandos. Pulsando elenterLa tecla le dice al shell que vaya y deje que el sistema ejecute el comando.

Este es un comportamiento normal y aceptado, pero algo que probablemente sorprendió a los primeros usuarios de UNIX.

Las combinaciones de teclado le permiten editar rápidamente sin tener que utilizar las teclas de flecha:

  • ctrl+dpara eliminar el carácter seleccionado actualmente
  • ctrl+fpara ir al personaje de la derecha
  • ctrl+bpara ir al personaje de la izquierda

Autocompletar

Una buena característica de Bash cuando se mueve por el sistema de archivos es el autocompletado. Intenta escribircd Docy presione eltabpara hacer que Bash autocomplete eso paracd Documents. Si hay varias opciones para esos primeros caracteres, Bash le devolverá la lista, por lo que puede escribir un par de caracteres más para ayudarlo a eliminar la ambigüedad y presionartabde nuevo para completar.

El shell puede autocompletar nombres de archivos, pero también nombres de comandos.

Comandos de shell

Usando el shell podemos ejecutar comandos disponibles en el sistema. Podemos prefijar los comandos con la ruta completa (p. Ej./bin/lspara listar archivos en una carpeta) pero el shell tiene el concepto decaminopara que podamos escribirlsy sabe dónde se encuentran la mayoría de los comandos (y podemos agregar carpetas a esta ruta configurándola).

Los comandos aceptan argumentos. Por ejemplols /binenumerará todos los archivos en el/bincarpeta.

Los parámetros están precedidos por un guión-, me gustals -aque dicelspara mostrar también archivos ocultos. Los archivos ocultos por convención son archivos (y carpetas) que comienzan con un punto (.).

Comandos de shell comunes

Hay muchos comandos preinstalados en cualquier sistema, y varían considerablemente dependiendo de si se trata de Linux / macOS o incluso de la distribución de Linux que esté ejecutando.

Sin embargo, hagamos un breve resumen de los comandos de shell más comunes que puede ejecutar. Los shells no los proporcionan per se, sino que son comandos de línea de comandos que puede invocar a través de shells.

Siempre que tenga problemas, como si no sepa lo que hace un comando o no sepa cómo usarlo, utiliceman. Le permite obtener ayuda para todos los comandos que enumeraré y más. Correrman lspor ejemplo.

Estos son los comandos del sistema de archivos:

  • lspara listar archivos
  • cdpara cambiar de carpeta
  • rmpara eliminar un archivo o carpeta
  • mvpara mover un archivo a otra carpeta o cambiar el nombre de un archivo
  • cpcopiar un archivo
  • pwdpara mostrar el directorio de trabajo actual
  • mkdirpara crear una carpeta

Todos los archivos de un sistema de archivos Unix tienen permisos.chmodle permite cambiarlos (no entrar en él ahora), ychownte permite cambiar el archivodueño.

cat,tailygrepson dos comandos súper útiles para trabajar con archivos.

pico,nano,vimyemacsson editores comúnmente instalados.

whereisen macOS muestra dónde se encuentra un comando en el sistema.

Hay muchos más comandos, por supuesto, pero esos son algunos con los que puede encontrarse con más frecuencia.

Ejecutando comandos

lsycdson comandos, como mencioné, que se encuentran en el/bincarpeta. Puede ejecutar cualquier archivo, siempre que sea un archivo ejecutable, escribiendo su ruta completa, por ejemplo/bin/pwd. Los comandos no necesitan estar en el/bincarpeta, y puede ejecutar archivos ejecutables que están en su carpeta actual usando el./indicador de ruta.

Por ejemplo, si tiene unrunmearchivar en tu/Users/flavio/scriptscarpeta, puede ejecutar

cd /Users/flavio/scripts

y luego corre./runmepara ejecutarlo.

O podrías correr/Users/flavio/scripts/runmedesde Bash, donde sea que esté su carpeta actual.

Trabajos

Siempre que ejecute un comando, si es un programa de ejecución prolongada, su shell será completamente propiedad de ese comando. Puedes matar el comando usandoctrl-C.

En cualquier momento puedes correrjobspara ver los trabajos que está ejecutando y su estado.

$ jobs
Job	Group	State	Command
1	72292	stopped	ftp

Another useful command is ps which lists the processes running.

$ ps
  PID TTY           TIME CMD
19808 ttys000    0:00.56 /usr/local/bin/fish -l
65183 ttys001    0:04.34 -fish
72292 ttys001    0:00.01 ftp

top shows the processes and the resources they are consuming on your system.

A job or process can be killed using kill <PID>.

Command history

Pressing the up arrow key will show you the history of the commands you entered. Again, this is a shell feature. Pressing the down arrow key will let you navigate back and forth in time to see what commands you entered previously, and pressing enter will let you run that command again.

This is a quick access to the command history. Running the history command will show you all the commands entered in the shell:

When you start typing a command, Bash can autocomplete it by referencing a previously entered command in the history. Try it by pressing esc followed by tab.

To be honest I find the Fish shell implementation of this to be much better and easier

Setting your default shell

There are many more shells other than Bash. You have Fish, ZSH, TCSH, and others. Any user on the system can choose its own shell.

You set your default login shell by running the chsh -s chsh -s /bin/bash command. You’ll almost never need to do that, unless it was previously changed, for example with Fish:

chsh -s /usr/local/bin/fish

Customizing Bash

I noted before that you might (or might not) see your current working directory in the Bash prompt. Where is this determined? In the Bash configuration!

There’s a bit of confusion here because Bash uses a different configuration file for different scenarios, and it also reads multiple configuration files.

Let’s give some order to this confusion. First, there’s a big distinction whether Bash is initialized as a login shell or not. By login shell we mean that the system is not running a GUI (Graphical User Interface) and you log in to the system through the shell. That’s the case of servers, for example.

In this case, Bash loads this configuration file:

/etc/profile

and then looks in the user home folder and looks for one of these files, in order, executing the first one it finds:

~/.bash_profile
~/.bash_login
~/.profile

were ~ means your home folder (it’s automatically translated by Bash)

This means that if there’s a .bash_profile, ~/.bash_login and ~/.profile are never run, unless explicitly executed in .bash_profile.

If instead of being a login shell Bash is run like I do with macOS, for example, as a normal application, the configuration files change. Bash loads first /etc/bash.bashrc, then ~/.bashrc.

Environment variables

Sometimes you have programs that use environment variables. Those are values that you can set outside of the program, and alter the execution of the program itself. An API key, for example. Or the name of a file.

You can set an environment variable using the syntax

VARIABLE_NAME=variable_value

The value can contain white spaces, by using quotes

VARIABLE_NAME="variable value"

A bash script can use this value by prepending a dollar sign: $VARIABLE_NAME.

Also other programming languages commands can use environment variables, for example here’s how to read environment variables with Node.js.

The system sets up some environment variables for you, like

  • $HOME your home folder
  • $LOGNAME your user name
  • $SHELL the path to your default shell
  • $PATH the path where the shell looks for commands

You can inspect their value by prepending echo to them:

echo $LOGNAME # flavio
echo $HOME # /Users/flavio

A special environment variable: $PATH

I mentioned the $PATH variable. This is a list of folders where the shell will look into when you type a command. Folders are separated by a colon : and they are written in order - Bash will look into the first, search for the command you asked for, and run it if it finds it. Otherwise if goes to the next folder and so on.

My path is currently:

bash-5.0$ echo $PATH
/usr/local/bin:/usr/bin: /bin:/usr/sbin: /sbin:/usr/local/go/bin

You typically edit this in the ~/.bashrc file by prepending or appending items:

PATH = "$PATH:/Users/flavio/bin"

Aliases

Using aliases we can set up shortcuts for common commands. You can give a quick name to a complex combination of parameters, for example.

You define an alias using the syntax

alias <alias>=<command>

if there’s a space in the command you use quotes:

alias <alias>="<command>"

One alias I commonly add to my system is ll:

alias ll="ls --al"

You normally define aliases in your ~/.bashrc file.

Just be careful with quotes if you have variables in the command: using double quotes the variable is resolved at definition time, using single quotes it’s resolved at invokation time. Those 2 are different:

alias lsthis="ls $PWD"
alias lscurrent='ls $PWD'

$PWD refers to the current folder the shell is into. If you now navigate away to a new folder, lscurrent lists the files in the new folder, lsthis still lists the files in the folder you were when you defined the alias.

Advanced command line features

Wildcards

ls and many other commands can make great use of wildcards. You can list all files starting with image:

ls image*

Or all files ending with image:

ls *image

or all files that contain image inside the name:

ls *image*

Redirecting output and standard error error

By default, commands started in the shell print out both the output and errors back to the shell. This might not be what you want. You can decide to write the output to a file instead.

Actually, to a /different file/, because in Unix even the screen is considered to be a file. In particular,

  • 0 identifies the standard input
  • 1 identifies the standard output
  • 2 identifies the standard error

You can redirect the standard output to a file by appending 1> after a command, followed by a file name.

Using the same technique you can use 2> to redirect the standard error.

There is a shortcut > for 1>, since that is used quite a lot.

Example:

ls 1> list.txt 2> error.txt
ls > list.txt 2> error.txt

Another shortcut, &>, redirects /both/ standard output and standard error to a file.

ls &> output.txt

Another frequent thing is to redirect standard error to standard output using 2>&1.

Running a command in the background

You can tell Bash to run a program in the background without it taking control of the shell, by appending & after it:

top &

top is a command that lists the processes running, ordered by most resource consuming.

The application that would normally get control of the shell, is now started but nothing seems to happen. You can bring it back into focus by typing fg (aka *f*ore*g*round), but now we’re entering in the realm of processes and jobs which is a big topic on its own, but a quick overview:

When a command is running you can use ctrl-Z to pause it and bring it to the background. The shell comes back again in the foreground, and you can now run bg to move resume execution of that previously paused job.

When you are ready to go back to it, run fg to bring back that program in the foreground.

You can see all processes running using ps, and the list shows all the processes pid numbers. Using the paused process pid, you can bring to foreground a specific command, for example fg 72292. Same works for bg.

Queuing commands

You can instruct Bash to run a command right after another ends by separating them with a semicolon:

cd /bin; ls

You can repeat this to queue multiple commands in the same line.

Output redirection

A program can receive input from any file using the < operator, and save to a file the output using the > operator:

echo hello > result.txt
wc < result.txt

wc is a command that counts the words it receives as input.

Pipe

Using pipes, any command output can be used as input for a second command. Use the | operator to combine the two. In this example, wc gets its input from the output of echo hello:

echo hello | wc

Grouping commands

Use && to combine two commands using “and”. If the first command executes without problems, run the second, and so on.

Use || to combine two commands using “or”. If the first command executes without problems the second does not run.

! negates the next logical operation:

$ echo hello && echo test
hello
test
$ echo hello || echo test
hello
$ ! echo hello || echo test
hello
test

You can use parentheses to combine expressions to avoid confusion, and also to change the precedence:

$ ! (echo hello) || (echo test)
hello
test
$ ! (echo hello || echo test)
hello

Programming shells

One of the best features of a shell like Bash is its ability to create programs with it, by basically automating commands execution.

I’m going to write a separate guide on Bash scripting soon, which will be separate from this tutorial because the topic is really more in-depth than what I want to add in this introductory Bash guide.

A quick intro though: a script is a text file that starts with a line that indicates that it’s a shell script (and what shell it requires), followed by a list of commands, one per line. Example:

#!/bin/bash
ls

You can save this as a file myscript and make it executable using chmod +x myscript, and run it using ./myscript (./ means “current folder”).

Shell scripting is outside of the scope of this post, but I want you to know about this. Scripts can have control structures and many other cool things.

This same scripting strategy works for other shells, like Zsh:

#!/bin/zsh
ls

Download my free Linux Commands Handbook


More cli tutorials: