Hay una gran diferencia entre una palabra clave y una incorporada, en la forma en que Bash analiza su código. Antes de hablar sobre la diferencia, enumeremos todas las palabras clave y las siguientes:
Construidos:
$ compgen -b
. : [ alias bg bind break
builtin caller cd command compgen complete compopt
continue declare dirs disown echo enable eval
exec exit export false fc fg getopts
hash help history jobs kill let local
logout mapfile popd printf pushd pwd read
readarray readonly return set shift shopt source
suspend test times trap true type typeset
ulimit umask unalias unset wait
Palabras clave:
$ compgen -k
if then else elif fi case
esac for select while until do
done in function time { }
! [[ ]] coproc
Tenga en cuenta que, por ejemplo, [
es un incorporado y que [[
es una palabra clave. Usaré estos dos para ilustrar la diferencia a continuación, ya que son operadores bien conocidos: todos los conocen y los usan regularmente (o deberían).
Bash escanea y comprende una palabra clave muy temprano en su análisis. Esto permite, por ejemplo, lo siguiente:
string_with_spaces='some spaces here'
if [[ -n $string_with_spaces ]]; then
echo "The string is non-empty"
fi
Esto funciona bien y Bash saldrá feliz
The string is non-empty
Tenga en cuenta que no cité $string_with_spaces
. Mientras que lo siguiente:
string_with_spaces='some spaces here'
if [ -n $string_with_spaces ]; then
echo "The string is non-empty"
fi
muestra que Bash no está contento:
bash: [: too many arguments
¿Por qué funciona con palabras clave y no con builtins? porque cuando Bash analiza el código, ve [[
cuál es una palabra clave y comprende muy pronto que es especial. Por lo tanto, buscará el cierre ]]
y tratará el interior de una manera especial. Un builtin (o comando) se trata como un comando real que se llamará con argumentos. En este último ejemplo, bash entiende que debe ejecutar el comando [
con argumentos (se muestra uno por línea):
-n
some
spaces
here
]
ya que se produce la expansión variable, la eliminación de comillas, la expansión del nombre de ruta y la división de palabras. El comando [
resulta estar construido en el shell, por lo que lo ejecuta con estos argumentos, lo que resulta en un error, de ahí la queja.
En la práctica, verá que esta distinción permite un comportamiento sofisticado, que no sería posible con los builtins (o comandos).
Todavía en la práctica, ¿cómo puedes distinguir una palabra clave de una palabra clave? Este es un experimento divertido de realizar:
$ a='['
$ $a -d . ]
$ echo $?
0
Cuando Bash analiza la línea $a -d . ]
, no ve nada especial (es decir, sin alias, sin redireccionamientos, sin palabras clave), por lo que solo realiza una expansión variable. Después de expansiones variables, ve:
[ -d . ]
por lo que se ejecuta el comando (incorporado) [
con argumentos -d
, .
y ]
, que, por supuesto, es verdad (esto sólo comprueba si.
es un directorio).
Ahora mira:
$ a='[['
$ $a -d . ]]
bash: [[: command not found
Oh. Esto se debe a que cuando Bash ve esta línea, no ve nada especial y, por lo tanto, expande todas las variables y finalmente ve:
[[ -d . ]]
En este momento, las expansiones de alias y el escaneo de palabras clave se han realizado durante mucho tiempo y ya no se realizarán, por lo que Bash intenta encontrar el comando llamado [[
, no lo encuentra y se queja.
En la misma linea:
$ '[' -d . ]
$ echo $?
0
$ '[[' -d . ]]
bash: [[: command not found
y
$ \[ -d . ]
$ echo $?
0
$ \[[ -d . ]]
bash: [[: command not found
La expansión de alias es algo bastante especial también. Todos ustedes han hecho lo siguiente al menos una vez:
$ alias ll='ls -l'
$ ll
.... <list of files in long format> ....
$ \ll
bash: ll: command not found
$ 'll'
bash: ll: command not found
El razonamiento es el mismo: la expansión de alias ocurre mucho antes de la expansión variable y la eliminación de comillas.
Keyword vs Alias
¿Qué crees que sucede si definimos un alias como palabra clave?
$ alias mytest='[['
$ mytest -d . ]]
$ echo $?
0
¡Oh, funciona! ¡para que los alias se puedan usar para alias de palabras clave! bueno saber.
Conclusión: los builtins realmente se comportan como comandos: corresponden a una acción que se ejecuta con argumentos que se someten a expansión directa de variables y división de palabras y globbing. Realmente es como tener un comando externo en algún lugar /bin
o /usr/bin
que se llama con los argumentos dados después de la expansión de variables, etc. Tenga en cuenta que cuando digo es realmente como tener un comando externo , solo me refiero a los argumentos, división de palabras, globbing, expansión variable, etc. ¡Un incorporado puede modificar el estado interno del shell!
Las palabras clave, por otro lado, se escanean y se entienden muy temprano, y permiten un comportamiento sofisticado del shell: el shell podrá prohibir la división de palabras o la expansión del nombre de ruta, etc.
Ahora mire la lista de palabras clave y palabras clave e intente averiguar por qué algunas deben ser palabras clave.
!
es una palabra clave Parece que sería posible imitar su comportamiento con una función:
not() {
if "$@"; then
return false
else
return true
fi
}
pero esto prohibiría construcciones como:
$ ! ! true
$ echo $?
0
o
$ ! { true; }
echo $?
1
Lo mismo para time
: es más poderoso tener una palabra clave para que pueda cronometrar comandos compuestos complejos y tuberías con redireccionamientos:
$ time grep '^#' ~/.bashrc | { i=0; while read -r; do printf '%4d %s\n' "$((++i))" "$REPLY"; done; } > bashrc_numbered 2>/dev/null
Si fuera time
un mero comando (incluso incorporado), solo vería los argumentos grep
, ^#
y /home/gniourf/.bashrc
, cronometraría esto, y luego su salida pasaría por las partes restantes de la tubería. Pero con una palabra clave, ¡Bash puede manejar todo! ¡puede time
la tubería completa, incluidas las redirecciones! Si time
fuera un mero comando, no podríamos hacer:
$ time { printf 'hello '; echo world; }
Intentalo:
$ \time { printf 'hello '; echo world; }
bash: syntax error near unexpected token `}'
Intenta arreglarlo:
$ \time { printf 'hello '; echo world;
time: cannot run {: No such file or directory
Sin esperanza.
Palabra clave vs alias?
$ alias mytime=time
$ alias myls=ls
$ mytime myls
¿Qué crees que pasa?
Realmente, un builtin es como un comando, excepto que está construido en el shell, ¡mientras que una palabra clave es algo que permite un comportamiento sofisticado! podemos decir que es parte de la gramática del shell.