Plus que toute autre langue que je connais, j'ai "appris" Bash en googlant à chaque fois que j'ai besoin de quelque chose. Par conséquent, je peux assembler des petits scripts qui semblent fonctionner. Cependant, je ne sais pas vraiment ce qui se passe et j'espérais une introduction plus formelle à Bash en tant que langage de programmation. Par exemple: quel est l'ordre d'évaluation? quelles sont les règles de cadrage? Quelle est la discipline de frappe, par exemple, tout est-il une chaîne? Quel est l'état du programme - est-ce une attribution clé-valeur de chaînes aux noms de variables; y a-t-il plus que cela, par exemple la pile? Y a-t-il un tas? Etc.
J'ai pensé consulter le manuel GNU Bash pour ce genre d'idées, mais cela ne semble pas être ce que je veux; c'est plus une longue liste de sucre syntaxique plutôt qu'une explication du modèle sémantique de base. Les millions et un "tutoriels bash" en ligne ne sont que pires. Peut-être devrais-je d'abord étudier sh
et comprendre Bash comme un sucre syntaxique en plus de cela? Je ne sais pas si c'est un modèle précis, cependant.
Aucune suggestion?
EDIT: on m'a demandé de fournir des exemples de ce que je recherche idéalement. Un exemple assez extrême de ce que je considérerais comme une «sémantique formelle» est cet article sur «l'essence de JavaScript» . Le rapport Haskell 2010 est peut-être un exemple un peu moins formel .
la source
Réponses:
Un shell est une interface pour le système d'exploitation. C'est généralement un langage de programmation plus ou moins robuste à part entière, mais avec des fonctionnalités conçues pour faciliter l'interaction spécifique avec le système d'exploitation et le système de fichiers. La sémantique du shell POSIX (ci-après appelée simplement "le shell") est un peu un mutt, combinant certaines fonctionnalités de LISP (les expressions s ont beaucoup en commun avec le fractionnement de mots du shell ) et C (une grande partie de la syntaxe arithmétique du shell la sémantique vient de C).
L'autre racine de la syntaxe du shell vient de son éducation en tant que méli-mélo d'utilitaires UNIX individuels. La plupart de ce qui est souvent intégré au shell peut en fait être implémenté en tant que commandes externes. Il jette de nombreux néophytes shell pour une boucle lorsqu'ils se rendent compte que cela
/bin/[
existe sur de nombreux systèmes.$ if '/bin/[' -f '/bin/['; then echo t; fi # Tested as-is on OS X, without the `]` t
wat?
Cela a beaucoup plus de sens si vous regardez comment un shell est implémenté. Voici une implémentation que j'ai faite comme exercice. C'est en Python, mais j'espère que ce n'est un problème pour personne. Ce n'est pas très robuste, mais c'est instructif:
#!/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()
J'espère que ce qui précède montre clairement que le modèle d'exécution d'un shell est à peu près:
1. Expand words. 2. Assume the first word is a command. 3. Execute that command with the following words as arguments.
Expansion, résolution de commande, exécution. Toute la sémantique du shell est liée à l'une de ces trois choses, bien qu'elles soient beaucoup plus riches que l'implémentation que j'ai écrite ci-dessus.
Pas toutes les commandes
fork
. En fait, il existe une poignée de commandes qui n'ont pas beaucoup de sens implémentées en tant qu'externes (de sorte qu'elles devraientfork
), mais même celles-ci sont souvent disponibles en tant qu'externes pour une conformité POSIX stricte.Bash s'appuie sur cette base en ajoutant de nouvelles fonctionnalités et mots clés pour améliorer le shell POSIX. Il est presque compatible avec sh, et bash est si omniprésent que certains auteurs de scripts passent des années sans se rendre compte qu'un script peut ne pas fonctionner sur un système strict POSIXly. (Je me demande aussi comment les gens peuvent se soucier autant de la sémantique et du style d'un langage de programmation, et si peu de la sémantique et du style du shell, mais je diverge.)
Ordre d'évaluation
C'est un peu une question piège: Bash interprète les expressions dans sa syntaxe principale de gauche à droite, mais dans sa syntaxe arithmétique, il suit la priorité C. Les expressions diffèrent des extensions , cependant. De la
EXPANSION
section du manuel bash:Si vous comprenez le découpage de mots, l'expansion des chemins et l'expansion des paramètres, vous êtes sur la bonne voie pour comprendre l'essentiel de ce que fait bash. Notez que l'expansion du chemin après la séparation des mots est critique, car elle garantit qu'un fichier avec un espace dans son nom peut toujours être mis en correspondance avec un glob. C'est pourquoi une bonne utilisation des extensions glob est meilleure que l' analyse des commandes , en général.
Portée
Portée de la fonction
Tout comme l'ancien ECMAscript, le shell a une portée dynamique, sauf si vous déclarez explicitement des noms dans une fonction.
$ foo() { echo $x; } $ bar() { local x; echo $x; } $ foo $ bar $ x=123 $ foo 123 $ bar $ …
Environnement et "portée" du processus
Les sous-shells héritent des variables de leurs shells parents, mais d'autres types de processus n'héritent pas de noms non exportés.
$ 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
Vous pouvez combiner ces règles de portée:
$ foo() { > local -x bar=123 # Export foo, but only in this scope > bash -c 'echo $bar' > } $ foo 123 $ echo $bar $
Discipline de frappe
Hum, types. Ouais. Bash n'a vraiment pas de types, et tout se développe en une chaîne (ou peut-être un mot serait plus approprié.) Mais examinons les différents types d'expansion.
Cordes
Presque tout peut être traité comme une chaîne. Les mots nus en bash sont des chaînes dont la signification dépend entièrement de l'expansion qui lui est appliquée.
Aucune expansionCela peut valoir la peine de démontrer qu'un simple mot n'est en réalité qu'un mot, et que les citations n'y changent rien.
Expansion de sous-chaîne$ 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
Pour plus d'informations sur les extensions, lisez la
Parameter Expansion
section du manuel. C'est assez puissant.Entiers et expressions arithmétiques
Vous pouvez imprégner les noms de l'attribut entier pour indiquer au shell de traiter le côté droit des expressions d'affectation comme de l'arithmétique. Ensuite, lorsque le paramètre se développe, il sera évalué comme un nombre entier avant de se développer en… une chaîne.
$ 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
Tableaux
Arguments et paramètres de positionAvant de parler de tableaux, il peut être utile de discuter des paramètres de position. Les arguments à un script shell sont accessibles en utilisant des paramètres numérotés,
$1
,$2
,$3
, etc. Vous pouvez accéder à tous ces paramètres à la fois à l' aide"$@"
, l' expansion qui a beaucoup de choses en commun avec les tableaux. Vous pouvez définir et modifier les paramètres de position à l'aide des fonctions intégréesset
oushift
, ou simplement en invoquant le shell ou une fonction shell avec ces paramètres:$ 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
Le manuel bash fait également parfois référence à
Tableaux$0
un paramètre de position. Je trouve cela déroutant, car il ne l'inclut pas dans le nombre d'arguments$#
, mais c'est un paramètre numéroté, donc meh.$0
est le nom du shell ou du script shell actuel.La syntaxe des tableaux est modélisée d'après les paramètres de position, il est donc généralement sain de considérer les tableaux comme une sorte nommée de "paramètres de position externes", si vous le souhaitez. Les tableaux peuvent être déclarés à l'aide des approches suivantes:
Vous pouvez accéder aux éléments du tableau par index:
$ echo "${foo[1]}" element1
Vous pouvez découper des tableaux:
$ printf '"%s"\n' "${foo[@]:1}" "element1" "element2"
Si vous traitez un tableau comme un paramètre normal, vous obtiendrez l'index zéro.
$ echo "$baz" element0 $ echo "$bar" # Even if the zeroth index isn't set $ …
Si vous utilisez des guillemets ou des barres obliques inverses pour empêcher la division des mots, le tableau conservera la division des mots spécifiée:
$ foo=( 'elementa b c' 'd e f' ) $ echo "${#foo[@]}" 2
La principale différence entre les tableaux et les paramètres de position est:
$12
est défini, vous pouvez être sûr qu'il$11
est également défini. (Il pourrait être défini sur la chaîne vide, mais$#
ne sera pas inférieur à 12.) Si"${arr[12]}"
est défini, il n'y a aucune garantie qu'il"${arr[11]}"
soit défini, et la longueur du tableau pourrait être aussi petite que 1.shift
un tableau, vous devez le découper et le réaffecter, commearr=( "${arr[@]:1}" )
. Vous pourriez aussi faireunset arr[0]
, mais cela ferait le premier élément à l'index 1.Il est souvent pratique d'utiliser des extensions de chemins pour créer des tableaux de noms de fichiers:
$ dirs=( */ )
Commandes
Les commandes sont essentielles, mais elles sont également couvertes plus en profondeur que je ne peux le faire dans le manuel. Lisez la
SHELL GRAMMAR
section. Les différents types de commandes sont:$ startx
)$ yes | make config
) (lol)$ grep -qF foo file && sed 's/foo/bar/' file > newfile
)$ ( cd -P /var/www/webroot && echo "webroot is $PWD" )
)Modèle d'exécution
Le modèle d'exécution implique bien sûr à la fois un tas et une pile. Ceci est endémique à tous les programmes UNIX. Bash a également une pile d'appels pour les fonctions shell, visible via l'utilisation imbriquée de la fonction
caller
intégrée.Références:
SHELL GRAMMAR
section du manuel bashVeuillez faire des commentaires si vous souhaitez que je développe davantage dans une direction spécifique.
la source
yes | make config
;-) Mais sérieusement, une très bonne rédaction./bin/[
et/bin/test
c'est souvent la même application 2) "Supposons que le premier mot est une commande." - attendez-vous quand vous faites la mission ...execle
et interpoler les premiers mots dans l'environnement, mais cela rendrait encore un peu plus compliqué.a = 1
ne pas travailler).La réponse à votre question "Quelle est la discipline de frappe, par exemple tout est-il une chaîne" Les variables Bash sont des chaînes de caractères. Mais, Bash permet des opérations arithmétiques et des comparaisons sur des variables lorsque les variables sont des entiers. L'exception à la règle Les variables Bash sont des chaînes de caractères lorsque lesdites variables sont composées ou déclarées autrement
$ 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
Déclarez la signification des options:
la source
La page de manuel bash contient un peu plus d'informations que la plupart des pages de manuel et inclut une partie de ce que vous demandez. Mon hypothèse après plus d'une décennie de scripting bash est que, en raison de son histoire en tant qu'extension de sh, il a une syntaxe géniale (pour maintenir la compatibilité descendante avec sh).
FWIW, mon expérience a été comme la vôtre; bien que les différents livres (par exemple, O'Reilly "Learning the Bash Shell" et autres) aident à la syntaxe, il existe de nombreuses façons étranges de résoudre divers problèmes, et certains d'entre eux ne sont pas dans le livre et doivent être recherchés sur Google.
la source