Más que cualquier otro idioma que conozco, he "aprendido" Bash buscando en Google cada vez que necesito algo. En consecuencia, puedo combinar pequeños guiones que parecen funcionar. Sin embargo, realmente no sé qué está pasando y esperaba una introducción más formal a Bash como lenguaje de programación. Por ejemplo: ¿Cuál es la orden de evaluación? ¿Cuáles son las reglas de alcance? ¿Cuál es la disciplina de mecanografía, por ejemplo, es todo una cadena? ¿Cuál es el estado del programa? ¿Es una asignación de valores clave de cadenas a nombres de variables? ¿Hay más que eso, por ejemplo, la pila? ¿Hay un montón? Y así.
Pensé en consultar el manual de GNU Bash para este tipo de información, pero no parece ser lo que quiero; es más una larga lista de azúcar sintáctico que una explicación del modelo semántico central. Los "tutoriales de bash" en línea del millón y uno son solo peores. ¿Quizás debería estudiar primero sh
y entender Bash como un azúcar sintáctico además de esto? Sin embargo, no sé si este es un modelo preciso.
¿Alguna sugerencia?
EDITAR: Me han pedido que proporcione ejemplos de lo que idealmente estoy buscando. Un ejemplo bastante extremo de lo que yo consideraría una "semántica formal" es este artículo sobre "la esencia de JavaScript" . Quizás un ejemplo un poco menos formal es el informe Haskell 2010 .
fuente
Respuestas:
Un shell es una interfaz para el sistema operativo. Por lo general, es un lenguaje de programación más o menos robusto por derecho propio, pero con características diseñadas para facilitar la interacción específicamente con el sistema operativo y el sistema de archivos. La semántica del shell POSIX (en lo sucesivo denominado "el shell") es un poco mutt, combinando algunas características de LISP (las expresiones s tienen mucho en común con la división de palabras del shell ) y C (gran parte de la sintaxis aritmética del shell la semántica viene de C).
La otra raíz de la sintaxis del shell proviene de su crianza como una mezcolanza de utilidades individuales de UNIX. La mayoría de los elementos integrados en el shell se pueden implementar como comandos externos. A muchos neófitos de caparazón les da un vuelco cuando se dan cuenta de que
/bin/[
existe en muchos sistemas.$ if '/bin/[' -f '/bin/['; then echo t; fi # Tested as-is on OS X, without the `]` t
¿Wat?
Esto tiene mucho más sentido si observa cómo se implementa un shell. Aquí hay una implementación que hice como ejercicio. Está en Python, pero espero que no sea un problema para nadie. No es terriblemente robusto, pero es instructivo:
#!/usr/bin/env python from __future__ import print_function import os, sys '''Hacky barebones shell.''' try: input=raw_input except NameError: pass def main(): while True: cmd = input('prompt> ') args = cmd.split() if not args: continue cpid = os.fork() if cpid == 0: # We're in a child process os.execl(args[0], *args) else: os.waitpid(cpid, 0) if __name__ == '__main__': main()
Espero que lo anterior deje en claro que el modelo de ejecución de un shell es prácticamente:
1. Expand words. 2. Assume the first word is a command. 3. Execute that command with the following words as arguments.
Expansión, resolución de comandos, ejecución. Toda la semántica del shell está ligada a una de estas tres cosas, aunque son mucho más ricas que la implementación que escribí anteriormente.
No todos los comandos
fork
. De hecho, hay un puñado de comandos que no tienen mucho sentido implementados como externos (de modo que tendrían que hacerlofork
), pero incluso esos a menudo están disponibles como externos para el estricto cumplimiento de POSIX.Bash se basa en esta base agregando nuevas funciones y palabras clave para mejorar el shell POSIX. Es casi compatible con sh, y bash es tan omnipresente que algunos autores de guiones pasan años sin darse cuenta de que es posible que un guión no funcione en un sistema POSIX estrictamente estricto. (También me pregunto cómo la gente puede preocuparse tanto por la semántica y el estilo de un lenguaje de programación, y tan poco por la semántica y el estilo del shell, pero diverjo).
Orden de evaluación
Esta es una pregunta un poco capciosa: Bash interpreta las expresiones en su sintaxis principal de izquierda a derecha, pero en su sintaxis aritmética sigue la precedencia C. Sin embargo, las expresiones difieren de las expansiones . De la
EXPANSION
sección del manual de bash:Si comprende la división de palabras, la expansión de nombre de ruta y la expansión de parámetros, está bien encaminado para comprender la mayor parte de lo que hace bash. Tenga en cuenta que la expansión del nombre de la ruta que viene después de la división de palabras es crítica, porque asegura que un archivo con espacios en blanco en su nombre aún pueda coincidir con un glob. Esta es la razón por la que un buen uso de las expansiones globales es mejor que analizar los comandos , en general.
Alcance
Alcance de la función
Al igual que el antiguo ECMAscript, el shell tiene un alcance dinámico a menos que declare explícitamente nombres dentro de una función.
$ foo() { echo $x; } $ bar() { local x; echo $x; } $ foo $ bar $ x=123 $ foo 123 $ bar $ …
Medio ambiente y "alcance" del proceso
Las subcapas heredan las variables de sus capas principales, pero otros tipos de procesos no heredan nombres no exportados.
$ x=123 $ ( echo $x ) 123 $ bash -c 'echo $x' $ export x $ bash -c 'echo $x' 123 $ y=123 bash -c 'echo $y' # another way to transiently export a name 123
Puede combinar estas reglas de alcance:
$ foo() { > local -x bar=123 # Export foo, but only in this scope > bash -c 'echo $bar' > } $ foo 123 $ echo $bar $
Disciplina de mecanografía
Um, tipos. Si. Bash realmente no tiene tipos, y todo se expande a una cadena (o quizás una palabra sería más apropiada). Pero examinemos los diferentes tipos de expansiones.
Instrumentos de cuerda
Prácticamente cualquier cosa puede tratarse como una cuerda. Las palabras desnudas en bash son cadenas cuyo significado depende completamente de la expansión que se le aplique.
Sin expansiónPuede valer la pena demostrar que una palabra simple es realmente solo una palabra, y que las comillas no cambian nada de eso.
Expansión de subcadena$ echo foo foo $ 'echo' foo foo $ "echo" foo foo
$ fail='echoes' $ set -x # So we can see what's going on $ "${fail:0:-2}" Hello World + echo Hello World Hello World
Para más información sobre expansiones, lea la
Parameter Expansion
sección del manual. Es bastante poderoso.Enteros y expresiones aritméticas
Puede imbuir nombres con el atributo integer para decirle al shell que trate el lado derecho de las expresiones de asignación como aritmética. Luego, cuando el parámetro se expanda, se evaluará como matemático entero antes de expandirse a ... una cadena.
$ foo=10+10 $ echo $foo 10+10 $ declare -i foo $ foo=$foo # Must re-evaluate the assignment $ echo $foo 20 $ echo "${foo:0:1}" # Still just a string 2
Matrices
Argumentos y parámetros posicionalesAntes de hablar de matrices, podría valer la pena discutir los parámetros posicionales. Los argumentos de un script de shell se puede acceder mediante parámetros numerados,
$1
,$2
,$3
,, etc Usted puede acceder a todos estos parámetros a la vez usando"$@"
, que la expansión tiene muchas cosas en común con las matrices. Puede establecer y cambiar los parámetros posicionales utilizando las funciones internasset
oshift
, o simplemente invocando el shell o una función de shell con estos parámetros:$ bash -c 'for ((i=1;i<=$#;i++)); do > printf "\$%d => %s\n" "$i" "${@:i:1}" > done' -- foo bar baz $1 => foo $2 => bar $3 => baz $ showpp() { > local i > for ((i=1;i<=$#;i++)); do > printf '$%d => %s\n' "$i" "${@:i:1}" > done > } $ showpp foo bar baz $1 => foo $2 => bar $3 => baz $ showshift() { > shift 3 > showpp "$@" > } $ showshift foo bar baz biz quux xyzzy $1 => biz $2 => quux $3 => xyzzy
El manual de bash también se refiere a veces
Matrices$0
como parámetro posicional. Encuentro esto confuso, porque no lo incluye en el recuento de argumentos$#
, pero es un parámetro numerado, así que meh.$0
es el nombre del shell o el script de shell actual.La sintaxis de las matrices se modela a partir de parámetros posicionales, por lo que es más saludable pensar en las matrices como un tipo de "parámetros posicionales externos", si lo desea. Las matrices se pueden declarar utilizando los siguientes enfoques:
Puede acceder a los elementos de la matriz por índice:
$ echo "${foo[1]}" element1
Puede cortar matrices:
$ printf '"%s"\n' "${foo[@]:1}" "element1" "element2"
Si trata una matriz como un parámetro normal, obtendrá el índice cero.
$ echo "$baz" element0 $ echo "$bar" # Even if the zeroth index isn't set $ …
Si usa comillas o barras invertidas para evitar la división de palabras, la matriz mantendrá la división de palabras especificada:
$ foo=( 'elementa b c' 'd e f' ) $ echo "${#foo[@]}" 2
La principal diferencia entre matrices y parámetros posicionales son:
$12
está configurado, puede estar seguro de que$11
también lo está. (Se podría establecer en la cadena vacía, pero$#
no será menor que 12.) Si"${arr[12]}"
se establece, no hay garantía de que"${arr[11]}"
se establezca, y la longitud de la matriz podría ser tan pequeña como 1.shift
una matriz, debe dividirla y reasignarla, comoarr=( "${arr[@]:1}" )
. También podría hacerlounset arr[0]
, pero eso haría que el primer elemento en el índice 1.A menudo es conveniente utilizar expansiones de nombre de ruta para crear matrices de nombres de archivo:
$ dirs=( */ )
Comandos
Los comandos son clave, pero también están cubiertos con mayor profundidad que el manual. Lea la
SHELL GRAMMAR
sección. Los diferentes tipos de comandos son:$ startx
. Ej. )$ yes | make config
. Ej. ) (Lol)$ grep -qF foo file && sed 's/foo/bar/' file > newfile
. Ej. )$ ( cd -P /var/www/webroot && echo "webroot is $PWD" )
. Ej. )Modelo de ejecución
El modelo de ejecución, por supuesto, implica tanto un montón como una pila. Esto es endémico de todos los programas de UNIX. Bash también tiene una pila de llamadas para funciones de shell, visible mediante el uso anidado de la función
caller
incorporada.Referencias:
SHELL GRAMMAR
sección del manual de bashPor favor haga comentarios si quiere que me expanda más en una dirección específica.
fuente
yes | make config
;-) Pero en serio, una muy buena reseña./bin/[
y a/bin/test
menudo es la misma aplicación 2) "Suponga que la primera palabra es un comando". - esperar cuando hagas la asignación ...execle
e interpolar las primeras palabras en el entorno, pero eso aún lo haría un poco más complicado.a = 1
no trabajar).La respuesta a su pregunta "¿Cuál es la disciplina de mecanografía, por ejemplo, es todo una cadena?" Las variables Bash son cadenas de caracteres. Pero, Bash permite operaciones aritméticas y comparaciones de variables cuando las variables son números enteros. La excepción a la regla de las variables de Bash son las cadenas de caracteres es cuando dichas variables se componen o declaran de otra manera
$ A=10/2 $ echo "A = $A" # Variable A acting like a String. A = 10/2 $ B=1 $ let B="$B+1" # Let is internal to bash. $ echo "B = $B" # One is added to B was Behaving as an integer. B = 2 $ A=1024 # A Defaults to string $ B=${A/24/STRING01} # Substitute "24" with "STRING01". $ echo "B = $B" # $B STRING is a string B = 10STRING01 $ B=${A/24/STRING01} # Substitute "24" with "STRING01". $ declare -i B $ echo "B = $B" # Declaring a variable with non-integers in it doesn't change the contents. B = 10STRING01 $ B=${B/STRING01/24} # Substitute "STRING01" with "24". $ echo "B = $B" B = 1024 $ declare -i B=10/2 # Declare B and assigning it an integer value $ echo "B = $B" # Variable B behaving as an Integer B = 5
Declarar los significados de las opciones:
fuente
La página de manual de bash tiene bastante más información que la mayoría de las páginas de manual e incluye algo de lo que está pidiendo. Mi suposición después de más de una década de scripting bash es que, debido a su 'historia como una extensión de sh, tiene una sintaxis funky (para mantener la compatibilidad con versiones anteriores de sh).
FWIW, mi experiencia ha sido como la suya; aunque los diversos libros (por ejemplo, O'Reilly "Learning the Bash Shell" y similares) ayudan con la sintaxis, hay muchas formas extrañas de resolver varios problemas, y algunos de ellos no están en el libro y deben buscarse en Google.
fuente