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.
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.
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 ;
.
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.
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.
~/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.
~/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.
~/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.
~/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
.
~/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
.
~/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.
~/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.
~/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.
~/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.
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.
~/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
.
~/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.
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.
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.