Software Reactionary - por fschmidt

Tutorial de Bash Reactionary


Contenido


Introducción

contenido

Realmente no quiero escribir este tutorial, pero todos los tutoriales de Bash existentes son tan horribles que no tengo otra opción. Miré libros, sitios web y YouTube, todos horribles. No comienzan con lo básico. Incluyen todo tipo de cosas inútiles. Y no explican conceptos fundamentales. Así que no tengo otra opción que escribir esto para mi lección de Bash de Aprender Programación Reactionary.

Bash es un shell, uno de muchos, pero el que prefiero. Me centraré en Mac y Windows. No tengo Linux, y odio Linux, así que no lo discutiré. La mayor parte de Bash es igual en Mac y Windows, pero donde difieren, discutiré ambos.

Ejecutando Bash

contenido

Cómo accedes a Bash depende de tu sistema operativo. Si estás en un Mac, accedes a Bash a través del Terminal de Mac que se encuentra en "Aplicaciones > Utilidades > Terminal.app". Asegúrate de configurar el shell predeterminado a Bash. Si estás en Windows, instala MSYS2. El terminal predeterminado no es tan bueno, así que sugiero usar el Terminal de Windows.

Comenzando

contenido

Cuando inicio Bash en mi Mac veo:

Last login: Thu Jan 4 23:25:35 on ttys004 The default interactive shell is now zsh. To update your account to use zsh, please run `chsh -s /bin/zsh`. For more details, please visit https://support.apple.com/kb/HT208050. ~ $

En Windows - MSYS2 solo veo:

~ $

La línea con el $ es el indicador de comando. El cursor está al final de él, y si escribo, mi texto irá allí. Puedes tener texto diferente antes del $ lo cual está bien, pero la línea debe terminar con $. Si no es así, algo está mal.

Ahora escribe "qqq". Cuando digo escribe "lo que sea", debes presionar return/enter al final. Solo cuando presionas return/enter Bash procesará lo que escribiste. Ahora deberías ver:

~ $ qqq -bash: qqq: command not found ~ $

Bash no sabe qué significa "qqq" y lo dice. Ahora intenta lo siguiente... Nota que escribes lo que está después del $ y Bash debería responder como se muestra.

~ $ echo hi hi ~ $ echo how are you how are you ~ $ echo bye bye

El comando echo solo repite lo que viene después. Ahora presiona la flecha hacia arriba en tu teclado. Esto debería poner el comando anterior donde está tu cursor. Flecha hacia arriba de nuevo trae el comando anterior a ese. Prueba flecha hacia abajo y flecha hacia la izquierda y flecha hacia la derecha. Puedes usar esto para navegar a través de tu historial de comandos. La tecla de borrar también funciona para editar líneas. Y por supuesto puedes escribir. Cuando presionas return/enter, Bash obtendrá tu comando editado y lo procesará.

Cuando ingresas echo how are you, echo es el comando. Este comando tiene 3 argumentos: how, are, y you. Los comandos y argumentos están separados por espacios. No importa cuántos espacios, así que:

~ $ echo how are you how are you

echo solo devuelve los argumentos separados por un espacio.

~ $ echo one; echo two one two

Puedes poner múltiples comandos en una línea separados por un ;.

El Comando "man"

contenido

Ingresa:

~ $ man echo

Deberías obtener algo como:

ECHO(1) BSD General Commands Manual ECHO(1) NAME echo -- write arguments to the standard output SYNOPSIS echo [-n] [string ...] DESCRIPTION The echo utility writes any specified operands, separated by single blank (` ') characters and followed by a newline (`\n') character, to the stan- dard output. The following option is available: -n Do not print the trailing newline character. This may also be achieved by appending `\c' to the end of the string, as is done by iBCS2 compatible systems. Note that this option as well as the effect of `\c' are implementation-defined in IEEE Std 1003.1-2001 (``POSIX.1'') as amended by Cor. 1-2002. Applications aiming for maximum portability are strongly encouraged to use printf(1) to suppress the newline character. :

Pero si estás en Windows, es posible que no tengas man instalado. En ese caso, haz:

~ $ pacman -S man-db

para instalar man como se describe aquí y luego intenta man echo de nuevo.

El comando man muestra la documentación de los comandos. Desafortunadamente, tiene una interfaz de usuario tonta basada en memorizar teclas, así que solo te diré las pocas teclas que necesitas. Flecha hacia abajo y flecha hacia arriba se mueven hacia abajo y hacia arriba por una línea. La tecla de espacio se mueve hacia abajo por una página. Y lo más importante, escribir "q" sale y te lleva de vuelta a Bash. Solo tienes que memorizar esto.

Ahora intenta ingresar man man. No necesitas todo lo que se muestra, pero puedes ver cómo se ve una página de manual complicada. Puedes usar man para obtener la documentación de otros comandos a medida que los discuto.

Directorios

contenido

Deberías estar familiarizado con el Finder de Mac o el Explorador de Archivos de Windows, y deberías saber por esto que los directorios (también llamados "carpetas") están organizados en un árbol.

En Mac:

~ $ pwd /Users/fschmidt ~ $ open . ~ $

En Windows:

~ $ pwd /home/fschmidt ~ $ explorer . ~ $

Al usar Bash, siempre estás en algún directorio, llamado el directorio actual o el directorio de trabajo. pwd te muestra la ruta completa a este directorio. Haz man pwd para más detalles. open . debería abrir el Finder de Mac para el directorio actual, y explorer . debería abrir el Explorador de Archivos de Windows para el directorio actual.

Continuando en mi Mac:

~ $ mkdir learn ~ $ cd learn ~/learn $ pwd /Users/fschmidt/learn

mkdir crea un directorio en el directorio actual. Deberías poder ver el directorio creado en el Finder de Mac o el Explorador de Archivos de Windows. cd significa "cambiar directorio". Esto cambia el directorio actual. cd es un comando incorporado (incorporado en Bash), y man no es útil con comandos incorporados, así que en lugar de man cd, intenta help cd. Continuando...

~/learn $ pwd /Users/fschmidt/learn ~/learn $ ls ~/learn $ touch file1 ~/learn $ ls file1 ~/learn $ touch file2 ~/learn $ touch file3 ~/learn $ ls file1 file2 file3 ~/learn $ mkdir dir1 ~/learn $ ls dir1 file1 file2 file3 ~/learn $ ls -F dir1/ file1 file2 file3 ~/learn $ ls -a . .. dir1 file1 file2 file3 ~/learn $ ls -a -F ./ ../ dir1/ file1 file2 file3 ~/learn $ ls -aF ./ ../ dir1/ file1 file2 file3

ls lista archivos y touch crea un archivo vacío. Los argumentos que comienzan con "-" son opciones. Haz man ls para ver qué hacen las opciones que usé. -F agrega un "/" a los directorios, y -a muestra archivos que comienzan con "." que generalmente están ocultos. Las opciones se pueden combinar.

~/learn $ ls file1 file1 ~/learn $ ls qqq ls: qqq: No such file or directory ~/learn $ ls file1 qqq file2 ls: qqq: No such file or directory file1 file2 ~/learn $ ls dir1 ~/learn $ touch dir1/d1file ~/learn $ ls dir1 d1file ~/learn $ ls -d dir1 dir1 ~/learn $ ls file1 file2 dir1 file1 file2 dir1: d1file ~/learn $ ls -d file1 file2 dir1 dir1 file1 file2 ~/learn $ ls -dF file1 file2 dir1 dir1/ file1 file2

Sin argumentos de archivo, ls lista archivos en el directorio actual. Con argumentos de archivo, lista esos archivos si existen. Si el archivo es un directorio, listará lo que hay en el directorio a menos que se use la opción -d.

~/learn $ ls dir1 file1 file2 file3 ~/learn $ ls . dir1 file1 file2 file3 ~/learn $ ls -d . . ~/learn $ ls -dF . ./ ~/learn $ ls ./file1 ./file1 ~/learn $ ls dir1 d1file ~/learn $ ls ./dir1 d1file ~/learn $ pwd /Users/fschmidt/learn ~/learn $ cd . ~/learn $ pwd /Users/fschmidt/learn

. es el directorio actual.

~/learn $ pwd /Users/fschmidt/learn ~/learn $ cd dir1 ~/learn/dir1 $ pwd /Users/fschmidt/learn/dir1 ~/learn/dir1 $ ls . d1file ~/learn/dir1 $ ls .. dir1 file1 file2 file3 ~/learn/dir1 $ cd .. ~/learn $ pwd /Users/fschmidt/learn ~/learn $ cd dir1 ~/learn/dir1 $ pwd /Users/fschmidt/learn/dir1 ~/learn/dir1 $ cd ../.. ~ $ pwd /Users/fschmidt ~ $ cd learn ~/learn $ pwd /Users/fschmidt/learn

.. es el directorio padre.

~/learn $ echo * dir1 file1 file2 file3 ~/learn $ echo d* dir1 ~/learn $ echo f* file1 file2 file3 ~/learn $ echo *1 dir1 file1 ~/learn $ echo dir1/* dir1/d1file ~/learn $ echo */* dir1/d1file ~/learn $ echo qqq* qqq*

* hace coincidencia de comodines de archivos. Es importante entender que Bash hace la coincidencia de comodines y luego pasa los argumentos resultantes al comando. echo nunca ve el "*" a menos que no haya coincidencia.

~/learn $ ls * file1 file2 file3 dir1: d1file ~/learn $ ls -dF * dir1/ file1 file2 file3 ~/learn $ ls -dF d* dir1/ ~/learn $ ls -dF f* file1 file2 file3 ~/learn $ ls -dF *1 dir1/ file1 ~/learn $ ls dir1/* dir1/d1file ~/learn $ ls */* dir1/d1file ~/learn $ ls -dF qqq* ls: qqq*: No such file or directory

Debería ser autoexplicativo.

~/learn $ pwd /Users/fschmidt/learn ~/learn $ cd ~ ~ $ pwd /Users/fschmidt ~ $ cd learn/dir1 ~/learn/dir1 $ pwd /Users/fschmidt/learn/dir1 ~/learn/dir1 $ cd ~ $ pwd /Users/fschmidt ~ $ cd ~/learn ~/learn $ pwd /Users/fschmidt/learn ~/learn $ echo ~ /Users/fschmidt ~/learn $ echo . . ~/learn $ echo .. ..

~ significa tu directorio de inicio. cd sin argumentos es lo mismo que cd ~. ~ se expande en tu directorio de inicio por Bash.

~/learn $ ls -ltF total 0 drwxr-xr-x 3 fschmidt staff 96 Jan 5 02:33 dir1/ -rw-r--r-- 1 fschmidt staff 0 Jan 5 02:21 file3 -rw-r--r-- 1 fschmidt staff 0 Jan 5 02:21 file2 -rw-r--r-- 1 fschmidt staff 0 Jan 5 02:21 file1

-l te da este formato técnico feo. Obtienes la fecha en que el archivo fue modificado por última vez. Antes de la fecha está el tamaño del archivo. -t ordena por fecha descendente.

Por último, describiré la autocompletación. Escribo echo d sin enter/return pero en su lugar presiono la tecla tab. Se autocompleta a echo dir1/. Presiono tab de nuevo y se autocompleta a echo dir1/d1file. Presionar tab mientras ingresas un archivo o directorio hace que Bash intente autocompletar usando nombres de archivo coincidentes. Si ingreso echo f y presiono tab, obtengo echo file. No sabe cuál elegir a continuación. Otra tab solo emite un pitido. Y otra tab muestra las opciones así:

~/learn $ echo file file1 file2 file3 ~/learn $ echo file

En general, puedes presionar tab en cualquier momento mientras ingresas un nombre de archivo y ver qué sucede. La autocompletación ahorra mucho tecleo.

Trabajando con Archivos

contenido
~/learn $ ls -F dir1/ file1 file2 file3 ~/learn $ cp file1 copied ~/learn $ ls -F copied dir1/ file1 file2 file3 ~/learn $ mv copied moved ~/learn $ ls -F dir1/ file1 file2 file3 moved ~/learn $ rm moved ~/learn $ ls -F dir1/ file1 file2 file3

cp copia archivos o directorios. mv mueve archivos o directorios. rm elimina archivos o directorios. Consulta las páginas de man de estos comandos para más detalles.

~/learn $ ls -F dir1/ file1 file2 file3 ~/learn $ mkdir dir2 ~/learn $ touch dir2/d2file ~/learn $ ls -F dir1/ dir2/ file1 file2 file3 ~/learn $ ls dir2 d2file ~/learn $ rm dir2 rm: dir2: is a directory ~/learn $ rm -d dir2 rm: dir2: Directory not empty ~/learn $ rm dir2/d2file ~/learn $ rm -d dir2 ~/learn $ ls -F dir1/ file1 file2 file3 ~/learn $ ls -F dir1/ file1 file2 file3 ~/learn $ mkdir dir2 ~/learn $ touch dir2/d2file ~/learn $ ls -F dir1/ dir2/ file1 file2 file3 ~/learn $ rm -r dir2 ~/learn $ ls -F dir1/ file1 file2 file3 ~/learn $ ls -F dir1/ file1 file2 file3 ~/learn $ cp dir1 dir2 cp: dir1 is a directory (not copied). ~/learn $ cp -r dir1 dir2 ~/learn $ ls -F dir1/ dir2/ file1 file2 file3 ~/learn $ ls dir2 d1file ~/learn $ cp f* dir2 ~/learn $ ls dir2 d1file file1 file2 file3 ~/learn $ rm -r dir2 ~/learn $ ls -F dir1/ file1 file2 file3 ~/learn $ ls -F dir1/ file1 file2 file3 ~/learn $ mkdir dir2 ~/learn $ cp -r dir1 dir2 ~/learn $ ls -F dir2 dir1/ ~/learn $ ls -F dir2/dir1 d1file ~/learn $ rm -r dir2 ~/learn $ ls -F dir1/ file1 file2 file3

Podría explicar todo esto, pero no lo haré. Deberías aprender a entender los comandos y sus opciones usando man y jugando con ellos. No continúes hasta que entiendas completamente lo anterior.

Citas

contenido
~/learn $ echo a b a b ~/learn $ echo "a b" a b ~/learn $ echo 'a b' a b ~/learn $ echo "a b" c a b c

Bash trata el texto entre comillas como un argumento. Así que en echo a b, echo tiene dos argumentos: "a" y "b". En echo "a b", echo tiene un argumento: "a b". En echo 'a b', echo tiene un argumento: "a b". En echo "a b" c, echo tiene dos argumentos: "a b" y "c".

~/learn $ echo a\ \ \ b a b

Fuera de las comillas, \ no se trata como un separador, sino que se trata como un carácter de espacio que es parte del argumento.

Variables

contenido
~/learn $ echo $X ~/learn $ X="some text" ~/learn $ echo $X some text ~/learn $ echo "X is: $X" X is: some text ~/learn $ echo 'X is: $X' X is: $X ~/learn $ X="$X and more" ~/learn $ echo $X some text and more

Aquí X es una variable. Obtienes su valor con $X. Esto también funciona dentro de comillas dobles pero no dentro de comillas simples.

Hay variables especiales llamadas variables de entorno que son utilizadas por Bash.

~/learn $ echo $PATH /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/fschmidt/Dropbox/bin:/Users/fschmidt/hg/luan/scripts:/usr/local/opt/postgresql@9.5/bin ~/learn $ which ls /bin/ls ~/learn $ cd /bin /bin $ pwd /bin /bin $ ls [ dd launchctl pwd test bash df link rm unlink cat echo ln rmdir wait4path chmod ed ls sh zsh cp expr mkdir sleep csh hostname mv stty dash kill pax sync date ksh ps tcsh /bin $ ls -F [* dd* launchctl* pwd* test* bash* df* link* rm* unlink* cat* echo* ln* rmdir* wait4path* chmod* ed* ls* sh* zsh* cp* expr* mkdir* sleep* csh* hostname* mv* stty* dash* kill* pax* sync* date* ksh* ps* tcsh* /bin $ cd ~/learn ~/learn $

PATH es una variable de entorno que contiene una lista de directorios separados por : que son buscados para comandos por Bash. El comando which muestra la ruta completa a un comando. ls -F agrega un * a los archivos ejecutables.

~/learn $ subl file1 -bash: subl: command not found ~/learn $ "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl" file1 ~/learn $ PATH="$PATH:/Applications/Sublime Text.app/Contents/SharedSupport/bin" ~/learn $ echo $PATH /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/fschmidt/Dropbox/bin:/Users/fschmidt/hg/luan/scripts:/usr/local/opt/postgresql@9.5/bin:/Applications/Sublime Text.app/Contents/SharedSupport/bin ~/learn $ subl file1 ~/learn $

Aquí edito el archivo file1 con Sublime Text, primero usando la ruta completa, y luego agregando el directorio a PATH para que Bash pueda encontrar subl.

Tengo Microsoft Word en Windows. Desde el Símbolo del sistema de Windows (no Bash):

C:\Users\fschmidt>winword C:\Users\fschmidt>where winword C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE

winword ejecuta Microsoft Word. El comando where del Símbolo del sistema es como el comando which de Bash. Así que ahora en MSYS2:

~ $ winword bash: winword: command not found ~ $ echo $PATH /usr/local/bin:/usr/bin:/bin:/opt/bin:/c/Windows/System32:/c/Windows:/c/Windows/System32/Wbem:/c/Windows/System32/WindowsPowerShell/v1.0/:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/c/Program Files/TortoiseHg:/c/Program Files/Java/jdk1.8.0_202/bin ~ $ PATH="$PATH:/c/Program Files/Microsoft Office/root/Office16" ~ $ echo $PATH /usr/local/bin:/usr/bin:/bin:/opt/bin:/c/Windows/System32:/c/Windows:/c/Windows/System32/Wbem:/c/Windows/System32/WindowsPowerShell/v1.0/:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/c/Program Files/TortoiseHg:/c/Program Files/Java/jdk1.8.0_202/bin:/c/Program Files/Microsoft Office/root/Office16 ~ $ winword ~ $

Volviendo al Mac, hay otra forma de ejecutar aplicaciones encontradas en "Aplicaciones" del Finder simplemente como aplicaciones en lugar de como comandos.

~/learn $ open -a 'Sublime Text' file1

Otra variable de entorno útil es PS1 que controla el indicador de comando. Ya tengo esto configurado, pero si no lo tuviera:

Franklins-MacBook-Pro:learn fschmidt$ echo $PS1 \h:\W \u\$ Franklins-MacBook-Pro:learn fschmidt$ PS1="\w $ " ~/learn $ echo $PS1 \w $ ~/learn $

Google "bash PS1" para más información.

.bash_profile

contenido
~/learn $ cd ~ $ ls .bash_profile .bash_profile

Si .bash_profile no se encuentra, haz touch .bash_profile para crearlo. Este archivo contiene comandos de Bash que se ejecutan cuando Bash se inicia. Si ya tienes este archivo, es probable que contenga comentarios que comienzan con #. Los comentarios se ignoran así:

~ $ # línea de comentario, no hace nada ~ $ echo lo que sea # comentario al final de la línea whatever ~ $

Para editar .bash_profile en un Mac, puedes hacer:

~ $ open -a 'Sublime Text' .bash_profile

Para editar .bash_profile en Windows, puedes hacer:

~ $ notepad .bash_profile

Ahora intenta agregar esta línea a .bash_profile:

echo hello there

Ahora, cuando abras un nuevo terminal de Bash, deberías ver "hello there". .bash_profile se ejecuta cuando Bash se inicia abriendo un nuevo terminal de Bash.

Yo configuro PS1 y PATH en .bash_profile para tener el indicador de comando que quiero y acceso a los comandos que quiero. Sugiero que hagas que el comando de Sublime Text subl esté disponible en PATH.

El Comando "find"

contenido
~/learn $ find . . ./file3 ./file2 ./file1 ./dir1 ./dir1/d1file ~/learn $ find . -name 'file*' ./file3 ./file2 ./file1 ~/learn $ find . -name '*file' ./dir1/d1file ~/learn $ find . -name 'd*' ./dir1 ./dir1/d1file ~/learn $ find . -name '*1' -or -name '*2' ./file2 ./file1 ./dir1

find busca recursivamente archivos en un árbol de directorios. Nota que en este caso la coincidencia de comodines * no la está haciendo Bash, la está haciendo find. find tiene muchas opciones para buscar archivos y actuar sobre ellos, consulta man find.

Entrada y Salida

contenido
~/learn $ echo 'this is a test' >test.txt ~/learn $ ls -F dir1/ file1 file2 file3 test.txt ~/learn $ cat test.txt this is a test ~/learn $ echo 'this is another test' >test.txt ~/learn $ cat test.txt this is another test ~/learn $ echo 'another line' >>test.txt ~/learn $ cat test.txt this is another test another line ~/learn $ cat <test.txt this is another test another line ~/learn $ cat <<End >test.txt > I am typing this > and this > End ~/learn $ cat test.txt I am typing this and this ~/learn $ (echo one; echo two) >test.txt ~/learn $ cat test.txt one two

Todos los programas tienen entrada estándar, salida estándar y error estándar. Los programas escriben salida normal a la salida estándar y mensajes de error al error estándar. Por defecto, la entrada estándar proviene del terminal, y la salida estándar y el error estándar van al terminal, pero esto se puede cambiar. >file envía la salida estándar a file. >>file agrega la salida estándar a file. <file lee la entrada estándar de file. <<whatever lee la entrada estándar del texto que sigue hasta una línea con solo whatever. Los comandos se pueden combinar entre ( y ). Asegúrate de man cat para entender cómo funciona cat.

~/learn $ ls >ls.txt ~/learn $ cat ls.txt dir1 file1 file2 file3 ls.txt test.txt ~/learn $ ls -d f* q* >ls.txt ls: q*: No such file or directory ~/learn $ cat ls.txt file1 file2 file3 ~/learn $ ls -d f* q* 2>ls.txt file1 file2 file3 ~/learn $ cat ls.txt ls: q*: No such file or directory ~/learn $ ls -d f* q* | tee ls.txt ls: q*: No such file or directory file1 file2 file3 ~/learn $ cat ls.txt file1 file2 file3 ~/learn $ ls -d f* q* 2>&1 | tee ls.txt ls: q*: No such file or directory file1 file2 file3 ~/learn $ cat ls.txt ls: q*: No such file or directory file1 file2 file3

2>file envía el error estándar a file. | envía la salida estándar del comando anterior a la entrada estándar del comando siguiente. 2>&1 envía el error estándar a la salida estándar. tee file lee la entrada estándar y luego la escribe tanto en la salida estándar como en file.

~/learn $ find . -type f | wc -l 6

Hay 6 archivos en learn. Usa man para averiguar cómo funciona esto.

Teclas de Control

contenido
~/learn $ sleep 3 ~/learn $ sleep 30 ^C ~/learn $

sleep 3 duerme durante 3 segundos, lo que significa que no hace nada durante 3 segundos. Esperé 3 segundos para que este comando terminara. Luego ejecuté sleep 30 que dormiría durante 30 segundos, pero perdí la paciencia y presioné control+c que interrumpe el programa y sale de él. Puedes intentar control+c si alguna vez te quedas atascado esperando que un comando termine.

~/learn $ wc I am typing this and this now I will end my input with control+d 3 14 65 ~/learn $ wc this time I will use control+c to break out ^C ~/learn $

Control+d significa fin de la entrada.

Sustitución de Comandos

contenido
~/learn $ echo I am in $(pwd) I am in /Users/fschmidt/learn ~/learn $ echo this directory contains: $(ls) this directory contains: dir1 file1 file2 file3 ls.txt test.txt ~/learn $ echo this directory contains $(ls | wc -l) files this directory contains 6 files

cmd $(commands) usará la salida de commands como texto de argumento para cmd.

~/learn $ cat $(find . -type f) | wc -c 86

Los archivos en learn contienen un total de 86 bytes. Usa man para averiguar cómo funciona esto.

Ampersand

contenido
~/learn $ (sleep 5; echo done) & [1] 10080 ~/learn $ echo waiting waiting ~/learn $ done [1]+ Done ( sleep 5; echo done ) ~/learn $

Normalmente Bash espera a que un comando se complete antes de mostrar el indicador de comando y permitir la entrada. Pero terminar una línea de comando con & le dice a bash que no espere, sino que ejecute el comando en un proceso separado. Arriba en ~/learn $ echo waiting, escribí echo waiting. Pero en ~/learn $ done, no escribí done. En su lugar, esto fue producido por echo done después de 5 segundos. [1] 10080 me dice que se inició un proceso y [1]+ Done ( sleep 5; echo done ) me dice que el proceso terminó.

Esto es útil cuando no quieres esperar a que un comando termine. Considera esto en Windows:

~ $ notepad

Aquí no obtendrás un indicador de comando de nuevo hasta que salgas de Notepad porque Bash está esperando que este comando termine. Así que en su lugar haz:

~ $ notepad & [1] 2010 ~ $

Ahora Notepad se ejecutará y puedes continuar usando Bash.

Scripts de Shell

contenido

Haz un archivo llamado test.sh que contenga lo siguiente:

echo this is a shell script

Ahora desde Bash:

~/learn $ cat test.sh echo this is a shell script ~/learn $ ./test.sh -bash: ./test.sh: Permission denied ~/learn $ ls -F test.sh test.sh ~/learn $ chmod +x test.sh ~/learn $ ls -F test.sh test.sh* ~/learn $ ./test.sh this is a shell script ~/learn $

chmod +x file convierte file en un ejecutable que se puede ejecutar. Ahora editaré test.sh

~/learn $ # edit test.sh ~/learn $ cat test.sh nonsense echo this is a shell script ~/learn $ ./test.sh ./test.sh: line 1: nonsense: command not found this is a shell script ~/learn $ # edit test.sh ~/learn $ cat test.sh set -e nonsense echo this is a shell script ~/learn $ ./test.sh ./test.sh: line 2: nonsense: command not found ~/learn $

Por defecto, los scripts continúan ejecutándose después de un error. En scripts más largos, queremos que el script salga después de un error. set -e hace esto, consulta help set.

~/learn $ X=some ~/learn $ echo $X some ~/learn $ echo $Xthing ~/learn $ echo ${X}thing something ~/learn $ # edit test.sh ~/learn $ cat test.sh echo "\$* = $*" echo "\$# = $#" echo "\$0 = $0" echo "\$1 = $1" echo "\$2 = $2" echo "\$3 = $3" echo "\$4 = $4" echo "\$14 = $14" echo "\${14} = ${14}" echo "\$@ = $@" ./count.sh "$*" ./count.sh "$@" ~/learn $ ./test.sh a b "c d" $* = a b c d $# = 3 $0 = ./test.sh $1 = a $2 = b $3 = c d $4 = $14 = a4 ${14} = $@ = a b c d 1 3 ~/learn $ cat count.sh echo $# ~/learn $

Los scripts de Bash tienen variables definidas especiales. La diferencia entre $* y $@ es sutil, y generalmente solo usarás $*. $* devuelve todos los argumentos como una cadena mientras que $@ devuelve los argumentos por separado, pero esta distinción rara vez hace alguna diferencia.

Variables y Scripts

contenido
~/learn $ X=value ~/learn $ echo $X value ~/learn $ # edit test.sh ~/learn $ cat test.sh echo "\$X = $X" ~/learn $ ./test.sh $X = ~/learn $ export X ~/learn $ ./test.sh $X = value

Las variables se definen en el shell actual. Los scripts de shell se ejecutan en su propio shell. Así que por defecto, no ven las variables definidas en el terminal/shell padre. export var hace que var esté disponible en procesos descendientes, lo que significa disponible en scripts de shell. Es una buena idea hacer export PATH en .bash_profile para que tu PATH esté disponible para tus scripts.

~/learn $ X=terminal ~/learn $ echo $X terminal ~/learn $ # edit test.sh ~/learn $ cat test.sh X=script export X ~/learn $ ./test.sh ~/learn $ echo $X terminal ~/learn $ . test.sh ~/learn $ echo $X script

Puedes exportar una variable de padre a hijos pero no de hijos a padre. . script incluye el texto en el archivo script en el shell actual. En este caso, no se ejecuta en un shell separado. Esta es la única forma de que un script establezca variables en tu shell de terminal.

~/learn $ pwd /Users/fschmidt/learn ~/learn $ # edit test.sh ~/learn $ cat test.sh cd ~ ~/learn $ ./test.sh ~/learn $ pwd /Users/fschmidt/learn ~/learn $ . test.sh ~ $ pwd /Users/fschmidt ~ $ cd learn ~/learn $

Esto ilustra la diferencia entre ./script y . script.

Tus Scripts

contenido
~/learn $ echo $PATH /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/fschmidt/Dropbox/bin:/Users/fschmidt/hg/luan/scripts:/usr/local/opt/postgresql@9.5/bin:/Applications/Sublime Text.app/Contents/SharedSupport/bin ~/learn $ echo ~/Dropbox/bin /Users/fschmidt/Dropbox/bin ~/learn $ ls -F ~/Dropbox/bin/e /Users/fschmidt/Dropbox/bin/e* ~/learn $ cat ~/Dropbox/bin/e open -a 'Sublime Text' $* ~/learn $ e test.sh ~/learn $

Cuando escribes scripts útiles, ponlos en un directorio y agrega ese directorio a tu PATH. Yo uso ~/Dropbox/bin y tengo un script llamado e en ese directorio para editar archivos. Así que e test.sh me permite editar test.sh desde la línea de comandos.

Nota que Bash solo buscará en tu PATH scripts a menos que des una ruta explícita al script.

~/learn $ # edit test.sh ~/learn $ cat test.sh echo this is a shell script ~/learn $ test.sh -bash: test.sh: command not found ~/learn $ ./test.sh this is a shell script ~/learn $

Llamar a test.sh por sí solo falla porque no está en el PATH. Pero ./test.sh funciona porque es una ruta explícita.

Scripting Avanzado

contenido

Aquí hay un script más avanzado llamado undocx.sh que descomprime un archivo DOCX de Word.

#!/bin/bash set -e if [ $# -ne 1 ]; then echo "usage: $0 filename" exit 1 fi FILE="$1" NEWDIR=$(basename $FILE .docx) mkdir $NEWDIR unzip $FILE -d $NEWDIR export XMLLINT_INDENT=$'\t' for file in $(find $NEWDIR -name "*.xml" -o -name "*.rels"); do mv "$file" temp.xml xmllint --format temp.xml >"$file" done rm temp.xml

Bash es un lenguaje de programación completo que contiene todas las características habituales. Algunos comandos en mi script están bien explicados por man, pero algunos no lo están. En particular, la documentación para if y for es pobre. En casos como este, sugiero preguntar a ChatGPT así:

Por favor, explica la declaración "if" de Bash. Por favor, explica la declaración "for" de Bash.

ChatGPT conoce bien Bash. Confío en que ChatGPT explique detalles pero no para explicar conceptos fundamentales. También puedes intentar Google, pero ChatGPT es mejor que los programadores modernos.

Conclusión

contenido

Al menos el 90% de tu uso de Bash serán comandos simples que ingresas en el terminal. Intenta usar Bash tanto como sea posible en lugar de usar la GUI para que practiques usándolo. A menos que te conviertas en administrador del sistema, no usarás scripting avanzado mucho. Pero con una comprensión sólida de los conceptos básicos, deberías poder descubrir cómo leer o escribir scripts avanzados cuando sea necesario.