Introduction à Bash Shell

Bonjour à tous. Ceci est une traduction du livre de préparation à l'examen RedHat RHCE. À mon avis, il est très accessible sur les bases de bash.

Les scripts shell sont une science en soi. Sans entrer dans les détails de tout ce qui se passe «sous le capot», vous apprendrez à utiliser les éléments de base pour écrire vos propres scripts et à analyser ce qui se passe dans les scripts shell tiers.



Comprendre les éléments de base des scripts shell


En fait, un script shell est une liste de commandes qui sont exécutées séquentiellement, ainsi qu'une certaine logique qui permet au code d'être exécuté uniquement dans certaines conditions.

Pour comprendre les scripts shell complexes, il est recommandé de commencer par les scripts de base.

Ce qui suit est un script très simple:

#!/bin/bash # # #This is a script that greets the world # Usage: ./hello clear echo hello world exit 0 

Il contient plusieurs éléments qui devraient être utilisés dans tous les scripts. Pour commencer, il y a shebang - c'est la ligne #! / Bin / bash. Lorsque le script est lancé à partir du shell parent, il ouvre un sous-shell dans lequel les commandes spécifiées dans le script sont exécutées.

Ces commandes peuvent être interprétées de différentes manières. Afin de comprendre exactement comment ils doivent être interprétés, le shebang est utilisé. Dans l'exemple ci-dessus, shebang indique clairement que le script doit être exécuté par le shell bash.

D'autres coquilles peuvent également être indiquées. Par exemple, si votre script contient du code Perl, shebang devrait être #! / Usr / bin / perl. Commencer un script avec shebang est une bonne pratique; s'il est omis, le code du script sera exécuté par le même shell que celui utilisé pour exécuter le script.

Immédiatement après le shebang, il y a une partie expliquant de quoi parle le script. Quelques lignes de commentaires au début de chaque scénario sont une bonne idée. Dans un court script, il est souvent évident de savoir ce qu'il fait, mais à mesure que le script s'allonge et que plus de gens s'impliquent dans l'écriture et le soutiennent, il devient moins clair ce que les auteurs ont l'intention de faire.

Pour éviter cette situation, assurez-vous d'ajouter des lignes de commentaires commençant par chaque caractère #. Les commentaires peuvent être non seulement dans les premières lignes, mais aussi au début de chaque sous-section du script. Cela vous sera certainement utile si vous lisez votre script dans quelques mois!

Vous pouvez également commenter non seulement les sous-sections, mais également les lignes individuelles.

Quelle que soit la position dans laquelle il est utilisé, tout, du caractère # à la fin de la ligne, est un commentaire.

Après le bloc de commentaires, le corps du script est localisé. Dans l'exemple ci-dessus, ce sont plusieurs commandes qui sont exécutées séquentiellement. Le corps du script shell peut augmenter à mesure qu'il se développe.

À la fin du script, j'ai inclus l'instruction exit 0 . L'instruction exit indique au shell parent si le script a réussi. L'état de sortie de la dernière commande du script est l'état de sortie du script lui-même, sauf si l' exit 0 est utilisé à la fin du script.

Il est utile de savoir que vous pouvez travailler avec exit pour indiquer au shell parent comment les choses se sont passées.
Introduit dans le shell parent, l'écho $? vous permet d'interroger l'état de sortie du dernier script en cours d'exécution.
Après avoir créé le script, assurez-vous qu'il peut être exécuté. La façon la plus courante de procéder consiste à lui appliquer un bit d'exécution. Donc, si le nom du fichier de script est bonjour, utilisez la commande chmod + x ./hello pour le rendre exécutable.

Le script peut également être exécuté comme argument de la commande bash. Dans ce cas, entrez bash ./hello pour exécuter le script hello. Si le script est exécuté comme argument de la commande bash, le fichier de script n'a pas besoin d'être exécutable.

En fait, vous pouvez stocker le script n'importe où, mais si vous allez le stocker dans un répertoire qui n'est pas inclus dans la variable $ PATH, vous devez l'exécuter avec ./ devant le nom du script.

Tapez ./hello pour exécuter le script ou placez-le dans le répertoire standard inclus dans la variable $ PATH, par exemple, / usr / local / bin.

Vous pouvez également placer le script dans le répertoire / bin, après quoi il suffit d'entrer le nom du fichier n'importe où dans le système de fichiers et le script s'exécutera.

Exemple

À l'aide de vi / bin / datetime, créez un fichier nommé datetime dans le répertoire / bin. Collez ce contenu dans le fichier créé:

 #!/bin/bash #    .     ,      date who 

Après avoir enregistré le fichier, entrez chmod + x / bin / datetime pour donner au fichier l'autorisation de s'exécuter. Par exemple, accédez à votre répertoire personnel à l'aide de la commande cd ~ et entrez simplement datetime .

Allez, par exemple, dans le répertoire cd ~ home et entrez simplement datetime.

 [root@localhost ~]# datetime Sat Sep 28 00:33:41 EDT 2019 root tty1 2019-09-25 20:28 root pts/0 2019-09-27 20:07 (comp.corp.domain.ru) 

Utilisation de variables et d'entrées


Les scripts bash sont bien plus qu'une simple liste de commandes exécutées séquentiellement. L'une des bonnes choses à propos des scripts est qu'ils peuvent travailler avec des variables et des entrées pour rendre le script flexible. Dans cette section, vous apprendrez à travailler avec eux.

Utilisation de paramètres positionnels


Lors de l'exécution du script, vous pouvez utiliser les arguments. Un argument est tout ce que vous mettez derrière une commande de script. Les arguments peuvent être utilisés pour rendre le script plus flexible. Exécutez la commande useradd lisa . Dans cet exemple, la commande est useradd et son argument, lisa , indique ce qui doit être fait.

À la suite d'une telle commande, un utilisateur nommé lisa doit être créé.

Dans le script, le premier argument est $ 1 , le deuxième argument est $ 2, etc. Le listing 1 montre comment les arguments peuvent être utilisés. Essayez d'exécuter ce code en spécifiant tout nom d'utilisateur comme paramètre.

Listing 1

 #!/bin/bash # run this script with a few arguments echo The first argument is $1 echo The second argument is $2 echo The third argument is $3 

Les paramètres signifient l'entrée de données avant d'exécuter le script. Dans ce cas, j'ai spécifié lisa , lori et bob comme paramètres après le nom du script d'argument:

 [root@server1 ~]# ./argument lisa lori bob The first argument is lisa The second argument is lori The third argument is bob [root@server1 ~]# 

Si vous avez essayé d'exécuter l'exemple de code, vous remarquerez peut-être que son contenu n'est pas parfait. Si vous utilisez trois arguments lors de l'exécution du script à partir du Listing 1, cela fonctionnera très bien. Si vous n'utilisez que deux arguments, le troisième est généré sans la valeur $ 3.

Si vous utilisez quatre arguments, la quatrième valeur (qui sera stockée dans $ 4) ne sera jamais affichée. Donc, si vous souhaitez utiliser des arguments, vous feriez mieux d'utiliser une approche plus flexible.

Listing 2

 #!/bin/bash # run this script with a few arguments echo you have entered $# arguments for i in $@ do echo $i done exit 0 

Le listing 2 montre deux nouveaux éléments liés aux arguments:

  • $ # Est un compteur qui indique le nombre d'arguments utilisés lors de l'exécution du script.
  • $ @ Est une liste de tous les arguments qui ont été utilisés lors de l'exécution du script.

Pour répertorier les arguments qui ont été utilisés lors de l'exécution de ce script, la boucle for est utilisée. Dans les boucles for , les instructions sont exécutées tant que la condition est vraie. Dans ce scénario, la condition pour i dans $ @ signifie «pour chaque argument». Chaque fois que le script parcourt la boucle, la valeur de la variable $ @ est affectée à la variable $ i .

Donc, tant qu'il y a des arguments, le corps du script est exécuté.

Le corps de la boucle for commence toujours par do et se ferme done , et les commandes à exécuter sont répertoriées entre ces deux mots clés. Ainsi, l'exemple de script utilise echo pour afficher la valeur de chaque argument et s'arrête lorsqu'il n'y a plus d'arguments disponibles.

Essayons le script du Listing 2 dans cet exemple:

  1. Tapez l' argument vi pour créer le fichier d'arguments et copiez le contenu du script du Listing 2 dans ce fichier.
  2. Enregistrez le fichier et rendez-le exécutable.
  3. Exécutez la commande ./argument abc . Vous verrez que trois lignes sont affichées.
  4. Exécutez la commande ./argument abcdef . Vous verrez qu'en plus de abc, de f sera également affiché.

Variables


Une variable est une étiquette utilisée pour indiquer un emplacement spécifique dans la mémoire qui contient une valeur spécifique. Les variables peuvent être définies statiquement en utilisant NAME = value ou dynamiquement. Il existe deux solutions pour définir dynamiquement une variable:

  • Utilisez le mot clé read dans un script pour demander des données à l'utilisateur exécutant le script.
  • Utilisez la substitution de commandes pour utiliser le résultat de la commande et l'affecter à une variable. Par exemple, la commande date +% d-% m-% y affiche la date actuelle au format jour-mois-année. Pour ce faire dans un script, vous pouvez utiliser AUJOURD'HUI = $ (date +% d-% m-% y) . Pour substituer des commandes, il vous suffit de mettre la commande dont vous souhaitez utiliser le résultat entre les crochets.

Dans la section précédente sur les paramètres de position, vous avez appris comment affecter des arguments aux variables lors de l'exécution d'un script. Dans certains cas, il peut être plus efficace de demander des informations lorsque vous constatez qu'il manque quelque chose de substantiel. Le script ci-dessous montre comment procéder.

Listing 3. Exemple de script utilisant la commande read

 #!/bin/bash if [ -z $1 ]; then echo enter a text read TEXT else TEXT=$1 fi echo you have entered the text $TEXT exit 0 

Dans le script du Listing 3, l'opérateur if ... then ... else ... fi est utilisé pour tester l'existence de l'argument $ 1 . Cela se fait à l'aide de test (test est une commande distincte). La commande test peut être écrite de deux manières *: test ou [...] . Dans l'exemple, la ligne if [-z $ 1] ... est exécutée pour voir le test (check) -z $ 1 .

* - en fait trois sources (environ. Traducteur)

-z test vérifie si $ 1 existe ou non. En d'autres termes, la ligne if [-z $ 1] vérifie si $ 1 est vide, ce qui signifie qu'aucun argument n'a été fourni lors de l'exécution de ce script. Si c'est le cas, les commandes après l'instruction then sont exécutées.

Veuillez noter que lors de l'écriture de la commande de test avec des crochets, il est important d'utiliser des espaces après le crochet d'ouverture et avant le crochet de fermeture, la commande ne fonctionnera pas sans espaces.

Notez que l'instruction then suit immédiatement le test . Cela est possible car un point-virgule (;) est utilisé. Le point-virgule est un séparateur de commandes et peut remplacer une nouvelle ligne dans un script.

L'instruction then exécute deux commandes: la commande echo , qui affiche le message à l'écran, et la commande read .

La commande de lecture arrête le script afin que l'entrée utilisateur puisse être traitée et stockée dans la variable TEXT. Par conséquent, read TEXT place toutes les entrées utilisateur dans la variable TEXT, qui sera utilisée plus tard dans le script.

La partie suivante est représentée par l' instruction else . Les commandes après l' instruction else sont exécutées dans tous les autres cas, ce qui signifie dans ce cas "sinon, si l'argument a été fourni". Si tel est le cas, la variable TEXT est déterminée et la valeur actuelle de $ 1 lui est affectée.

Remarquez comment la variable est définie: immédiatement après le nom de la variable, il y a un signe = suivi de $ 1. Notez que vous ne devez jamais utiliser d'espaces lors de la définition de variables.

Ensuite, les conditions if sont fermées à l'aide de l'opérateur fi . Une fois la condition if terminée, vous savez avec certitude que la variable TEXT est définie et a une valeur. L'avant-dernière ligne du script lit la valeur de la variable TEXT et mappe cette valeur à STDOUT à l'aide de la commande echo . Notez que pour demander la valeur actuelle d'une variable, elle fait référence au nom de la variable, en commençant par le signe $ devant elle.

Vous pouvez vous entraîner à utiliser cet exemple lorsque vous travaillez avec une entrée.

  1. Ouvrez l'éditeur et créez un fichier appelé texte. Entrez le contenu du code du Listing 3 dans ce fichier.
  2. Écrivez le fichier sur le disque et exécutez chmod + x text pour le rendre exécutable.
  3. Exécutez le script en exécutant ./text et sans arguments supplémentaires. Vous verrez qu'il demande une entrée.
  4. Exécutez le script en utilisant " hello " comme argument (./text hello). Le résultat affichera "vous avez entré le texte bonjour" dans STDOUT.

Utilisation de conditions et de boucles


Comme vous l'avez déjà vu, les instructions conditionnelles peuvent être utilisées dans un script. Ces instructions conditionnelles ne sont exécutées que si une certaine condition est remplie.

Il existe plusieurs instructions conditionnelles et boucles dans bash qui sont souvent utilisées.

  • if ... then ... else - utilisé pour exécuter du code si une certaine condition est remplie
  • for - utilisé pour exécuter des commandes pour une plage de valeurs
  • while - utilisé pour exécuter du code si une certaine condition est remplie
  • avant - utilisé pour exécuter du code jusqu'à ce qu'une certaine condition soit remplie
  • cas - utilisé pour évaluer un nombre limité de valeurs spécifiques

si alors autrement


La construction if then else est courante pour évaluer des conditions spécifiques. Vous en avez déjà vu un exemple. Cette instruction conditionnelle est souvent utilisée avec la commande test . Cette commande vous permet de vérifier de nombreuses choses: par exemple, non seulement si un fichier existe, mais aussi comparer des fichiers, comparer des entiers et bien plus encore.
Vous pouvez en savoir plus sur test dans la référence avec la commande man test.

La construction de base de if est if ... then ... fi .

Il compare une condition, comme le montre l'exemple suivant:

 if [ -z $1 ] then echo no value provided fi 

Dans l'extrait 3, vous avez vu comment évaluer deux conditions, y compris sinon dans une expression. Le listing 4 montre comment évaluer plusieurs conditions de if à else . Ceci est utile si vous devez vérifier de nombreuses valeurs différentes.

Notez que cet exemple utilise également plusieurs commandes de test .

Listing 4 . Exemple avec if then else

 #!/bin/bash # run this script with one argument # the goal is to find out if the argument is a file or a directory if [ -f $1 ] then echo "$1 is a file" elif [ -d $1 ] then echo "$1 is a directory" else echo "I do not know what \$1 is" fi exit 0 

|| et &&


Au lieu d'écrire des instructions if ... complètes , vous pouvez utiliser les opérateurs logiques || ainsi que && . || est un "OU" logique et exécutera la deuxième partie de l'instruction uniquement si la première partie n'est pas vraie; && est un «ET» logique et exécutera la deuxième partie de l'instruction uniquement si la première partie est vraie.

Considérez ces deux lignes:

 [ -z $1 ] && echo no argument provided 

 ping -c 1 8.8.8.8 2>/dev/null || echo node is not available 

Le premier exemple vérifie si $ 1 est vide. Si cette vérification est correcte (ce qui signifie essentiellement que la commande se termine par le code de sortie 0), la deuxième commande est exécutée.

Dans le deuxième exemple, la commande ping est utilisée pour vérifier la disponibilité de l'hôte.
Cet exemple utilise un "OU" logique pour afficher le texte "le nœud n'est pas disponible" en cas d'échec de la commande ping .

Vous le trouverez souvent, au lieu de l' instruction conditionnelle if , && et || . Dans l'exercice ci-dessous, vous pouvez vous entraîner à utiliser des instructions conditionnelles en utilisant si ... alors ... else , ou && et || .

Faites de l'exercice . Utiliser si ... alors ... sinon

Dans cet exercice, vous allez travailler sur un script qui vérifie ce qu'est un fichier et ce qu'est un répertoire.

  1. Lancez l'éditeur et créez un script appelé filechk.
  2. Copiez le contenu du Listing 4 dans ce script.
  3. Exécutez quelques tests avec lui, tels que ./filechk / etc / hosts , ./filechck / usr , ./filechk fichier-non-existant .

Pour boucle


La boucle for est une excellente solution pour le traitement des plages de données. Dans le listing 5, vous pouvez voir le premier exemple avec for , où la plage est déterminée et traitée alors qu'il y a des valeurs brutes dans cette plage.

Listing 5

 #!/bin/bash # for (( COUNTER=100; COUNTER>1; COUNTER-- )); do echo $COUNTER done exit 0 

Une boucle for commence toujours par le mot clé for, suivie d'une condition qui doit être vérifiée. Ceci est suivi du mot clé do , suivi des commandes qui doivent être exécutées.Si la condition est vraie, la boucle se termine en utilisant le mot clé done .

Dans l'exemple du Listing 5, vous pouvez voir que la condition est une plage de nombres entre parenthèses affectée à la variable COUNTER.

Une petite explication

À l'intérieur ((...)) les expressions arithmétiques sont calculées et leur résultat est renvoyé. Par exemple, dans le cas le plus simple, la construction a = $ ((5 + 3)) affectera à la variable «a» la valeur de l'expression «5 + 3», ou 8. De plus, les doubles parenthèses permettent de travailler avec des variables dans le style du langage C.

Tout d'abord, la variable est initialisée à 100 et tant que la valeur est supérieure à 1, elle est soustraite 1 à chaque itération. Tant que la condition est vraie, la valeur de la variable $ COUNTER est affichée à l'aide de la commande echo .

Dans le Listing 6, vous pouvez voir l'un de mes one-liners préférés avec for . La plage est définie cette fois comme une séquence de nombres, commençant à 100 et atteignant 104.

Listing 6

 for i in {100..104}; do ping –c 1 192.168.4.$i >/dev/null && echo 192.168.4.$i is up; done 

Remarquez comment la plage est déterminée: vous spécifiez d'abord le premier nombre, puis deux points et indiquez le dernier nombre de la plage. De plus, avec pour i dans, pour chacun de ces nombres, la variable i est affectée. Chacun de ces numéros est affecté à la variable i , puis la commande ping est exécutée, où l'option -c 1 garantit qu'une seule demande est envoyée.

Le résultat de la commande ping n'est pas pris en compte, donc sa sortie est redirigée vers / dev / null. En fonction de l'état de sortie de la commande ping , une partie de l'expression && est exécutée. Ainsi, si l'hôte est disponible, une ligne s'affiche indiquant qu'il est en cours d'exécution.

Comprendre pendant et jusqu'à


Si l'instruction for que vous venez de lire est utile pour travailler avec des plages d'éléments, l'instruction while est utile lorsque vous souhaitez suivre quelque chose comme l'accessibilité des processus. Il existe également une instruction until , qui est exécutée tant que la condition vérifiée est fausse. Dans le Listing 7, vous pouvez lire comment while est utilisé pour surveiller l'activité du processus.
Remarque Je n'ai pas compris ce que fait ce script. Dans mon cas, CentOS 7 est utilisé et par défaut il n'y a pas de moniteur, bien que le script indique explicitement:
  utilisation: surveiller <nom du processus> 
Quelque part pendant une demi-heure, j'ai googlé le programme de moniteur pour CetOS, mais je ne l'ai pas trouvé. Et généralement, on ne sait pas quel moniteur latéral est ici si ps aux est utilisé. En tout cas, je n'ai pas compris ce que fait ce script. Une grande demande pour aider à résoudre ce problème est d'ajuster le texte et / ou le script.
Listing 7

 #!/bin/bash # # usage: monitor <processname> while ps aux | grep $1 | grep -v grep > /dev/tty11 do sleep 5 done clear echo your process has stopped logger $1 is no longer present mail -s "process $1 has stopped" root < . 

Le script du Listing 7 se compose de deux parties. Tout d'abord, il y a une boucle while . Deuxièmement, il y a tout ce qui doit être fait lorsque la boucle while n'est plus évaluée comme vraie.

Le cœur de la boucle while est la commande ps , qui a une valeur de $ 1 .

Notez l'utilisation de grep -v grep , qui exclut les lignes contenant la commande grep du résultat. N'oubliez pas que la commande ps inclura tous les processus en cours d'exécution, y compris la commande grep , à laquelle la sortie de la commande ps est transmise. Cela peut conduire à une fausse correspondance positive.

La sortie de la commande ps aux est redirigée vers / dev / tty11. Cela vous permet de lire les résultats de tty11 plus tard, si nécessaire, mais ils ne sont pas affichés par défaut.

Les instructions while sont suivies des commandes qui doivent être exécutées si la condition vérifiée est vraie. Dans ce cas, c'est la commande sleep 5 , qui suspend l'exécution du script pendant 5 secondes.

Tant que la condition de l'instruction while est vraie, la boucle continue de s'exécuter. Si la condition est fausse (ce qui signifie dans ce cas que le processus n'est plus disponible), le cycle s'arrête et les commandes qui le suivent peuvent être exécutées.

Vous devez être familiarisé avec toutes ces commandes, sauf la dernière. Dans la ligne mail -s "le processus $ 1 s'est arrêté" root <. le message est envoyé à l'utilisateur root à l'aide du système de messagerie interne, qui s'exécute sur Linux * par défaut. Commande de messagerieprend comme premier argument le sujet du message spécifié avec l' option -s .

* - au moins sur CentOS cela fonctionne par défaut. (at. traducteur)

Faites attention à <. à la fin de l'équipe.

Habituellement, lorsque vous utilisez la commande mail en mode interactif, un éditeur s'ouvre dans lequel vous pouvez écrire le corps du message. Cet éditeur est fermé, fournissant une ligne qui n'a qu'une période. Dans cette commande, un point est fourni en redirigeant STDIN. Cela permet au message d'être traité sans aucune exigence supplémentaire pour l'activité de l'utilisateur.

La boucle while est l'opposé de la boucle until , dont un exemple est illustré dans le Listing 8.jusqu'à ce que démarre une itération qui dure jusqu'à ce que la condition devienne vraie.

Dans le Listing 8, il est utilisé pour filtrer la sortie de la commande users par l'occurrence de $ 1 , qui sera le nom d'utilisateur. Tant que cette commande n'est pas correcte, l'itération continue. Lorsque le nom d'utilisateur est trouvé dans la sortie utilisateur, l'itération se ferme et après la boucle jusqu'à , les autres commandes sont exécutées.

Listing 8

 #!/bin/bash # until users | grep $1 > /dev/null do echo $1 is not logged in yet sleep 5 done echo $1 has just logged in mail -s "$1 has just logged in" root < . 

Comprendre le cas


La dernière boucle d'itération importante est le cas *. L' instruction case est utilisée pour évaluer un certain nombre de valeurs attendues. En particulier, les déclarations de cas sont importantes dans les scripts de démarrage Linux, qui dans les versions précédentes étaient utilisés pour démarrer les services.

* - est-ce un cycle?

Dans l' instruction case, vous définissez chaque argument concret que vous attendez, suivi de la commande à exécuter si cet argument a été utilisé.

Dans l'extrait 9, vous pouvez voir l' instruction case , qui était utilisée dans une version antérieure pour exécuter presque tous les services.

Listing 9

 case "$1" in start) start;; stop) rm -f $lockfile stop;; restart) restart;; reload) reload;; status) status ;; *) echo "Usage: $0 (start|stop|restart|reload|status)" ;; esac 

le boîtier a plusieurs caractéristiques. Vient d'abord la ligne - cas en séquence . Ceci est suivi d'une liste de toutes les valeurs possibles qui doivent être évaluées. Chaque élément est fermé par un support ) .

Ceci est suivi d'une liste de commandes à exécuter si un argument spécifique a été utilisé. La liste des commandes est fermée par un double point-virgule ;; peut être utilisé immédiatement après la dernière commande et peut être utilisé sur une ligne distincte.

Notez également que *) s'applique à tous les autres paramètres non spécifiés précédemment. Il s'agit d'un opérateur global.

La boucle d'itération de cas se termine par l' instruction esac .

Notez que les séquences en cas sont exécutées dans l'ordre. Lorsque la première correspondance est établie, l' instruction case n'évalue rien.

Dans le cadre de l'évaluation, des modèles similaires au modèle peuvent être utilisés. Ceci est montré dans *) une séquence qui correspond à tout. Mais vous pouvez également utiliser des séquences telles que start | Start | START) pour correspondre en utilisant un autre cas .

Débogage de scripts dans Bash


, , . , bash -x . , , , .

10 bash -x , , grep , , .

 [root@server1 ~]# bash -x 319.sh + grep Usage: grep [OPTION]... PATTERN [FILE]... Try 'grep --help' for more information. + users + echo is not logged in yet is not logged in yet + sleep 5 


, . , .

Source: https://habr.com/ru/post/fr471242/


All Articles