Habituellement, lorsque les gens mentionnent « langage de script shell », ce qui vient à l'esprit est bash, ksh, sh ou d'autres langages de script Linux/Unix similaires. Un langage de script est une autre façon de communiquer avec l'ordinateur. Avec une interface graphique à fenêtres (que ce soit Windows ou Linux, peu importe), l'utilisateur peut déplacer la souris et cliquer sur divers objets, tels que des boutons, des listes, des cases à cocher, etc. Mais cette méthode est très peu pratique chaque fois que l'utilisateur veut que l'ordinateur/le serveur effectue la même tâche (par exemple, convertir des photos par lots, ou télécharger de nouveaux films, MP3, etc.). Pour rendre toutes ces choses simples et automatisées, nous pouvons utiliser des scripts shell.
Certains langages de programmation, comme Pascal, FoxPro, C, Java, etc., doivent être compilés avant exécution. Ils nécessitent un compilateur approprié pour permettre à notre code d'accomplir une certaine tâche.

D'autres langages de programmation, tels que PHP, JavaScript, Visual Basic, etc., n'ont pas besoin de compilateur ; au lieu de cela, ils nécessitent un interpréteur, et nous pouvons exécuter des programmes sans compiler le code.
Les scripts shell sont aussi comme des interpréteurs, mais ils sont généralement utilisés pour appeler des programmes compilés externes. Ensuite, ils capturent les résultats de sortie, les codes de sortie et les traitent selon la situation.
L'un des langages de script shell les plus populaires dans le monde Linux est bash. Et je pense (ceci est mon propre avis) que la raison est que, par défaut, le shell bash permet aux utilisateurs de naviguer facilement dans les commandes d'historique (celles exécutées précédemment). En revanche, ksh nécessite quelques ajustements dans .profile, ou de mémoriser quelques combinaisons de touches « magiques » pour consulter l'historique et corriger les commandes.
Très bien, je pense que cette introduction suffit. Quant à savoir quel environnement vous convient le mieux, je vous laisse décider. À partir de maintenant, je ne parlerai que de bash et de ses scripts. Dans les exemples suivants, j'utiliserai CentOS 6.6 et bash-4.1.2. Veuillez vous assurer que vous avez la même version ou une version supérieure.
Flux de scripts shell
Le langage de script shell est semblable à une discussion avec plusieurs personnes. Vous devez simplement imaginer toutes les commandes comme des personnes qui peuvent vous aider à faire des choses, tant que vous le leur demandez de la bonne manière. Par exemple, vous voulez écrire un document. D'abord, vous avez besoin de papier. Ensuite, vous devez dire à quelqu'un le contenu pour qu'il puisse l'écrire pour vous. Enfin, vous voulez le stocker quelque part. Ou bien, disons que vous voulez construire une maison, vous devez donc embaucher les bonnes personnes pour déblayer le terrain. Une fois qu'elles ont dit « le travail est fait », d'autres ingénieurs peuvent vous aider à construire les murs. Enfin, lorsque ces ingénieurs vous disent aussi « le travail est fait », vous pouvez appeler les peintres pour décorer la maison. Que se passe-t-il si vous demandez aux peintres de décorer avant que les murs ne soient construits ? Je pense qu'ils commenceront à se plaindre. Presque toutes ces commandes à l'image des humains vont parler. Si elles terminent le travail sans aucun problème, elles vous diront « sortie standard ». Si elles ne peuvent pas faire ce que vous leur avez demandé — elles vous diront « erreur standard ». Donc, en fin de compte, toutes les commandes vous écoutent via « entrée standard ».
Exemple rapide — lorsque vous ouvrez un terminal Linux et tapez du texte — vous parlez à bash via « entrée standard ». Alors, demandons au shell bashwho am i (Qui suis-je ?)ensuite.
root@localhost ~]# who am i <--- 你通过标准输入对 bash shell 说 root pts/0 2015-04-22 20:17 (192.168.1.123) <--- bash shell通过标准输出回答你
Maintenant, demandons à bash quelque chose qu'il ne comprend pas :
[root@localhost ~]# blablabla <--- 哈,你又在和标准输入说话了 -bash: blablabla: command not found <--- bash通过标准错误在发牢骚了
Le premier mot avant le ':' est généralement la commande qui se plaint auprès de vous. En fait, chacun de ces flux a son propre numéro d'index (note du traducteur LCTT : numéro de descripteur de fichier) :
- Entrée standard (stdin) – 0
- Sortie standard (stdout) – 1
- Erreur standard (stderr) – 2
Si vous voulez vraiment savoir ce que la sortie d'une commande a dit, vous devez rediriger cette parole vers un fichier (en utilisant le signe supérieur à « > » et l'index du flux après la commande) :
[root@localhost ~]# blablabla 1> output.txt -bash: blablabla: command not found
Dans cet exemple, nous essayons de rediriger le flux 1 (stdout) vers un fichier nommé output.txt. Voyons ce qui a été fait au contenu du fichier ; vous pouvez le faire avec la commande cat :
[root@localhost ~]# cat output.txt [root@localhost ~]#
Il semble être vide. Bon, maintenant redirigeons le flux 2 (stderr):
[root@localhost ~]# blablabla 2> error.txt [root@localhost ~]#
Eh bien, nous voyons que les plaintes ont disparu. Vérifions ce fichier :
[root@localhost ~]# cat error.txt -bash: blablabla: command not found [root@localhost ~]#
Effectivement ! Nous voyons que toutes les plaintes ont été enregistrées dans le fichier errors.txt.
Parfois, une commande produit à la foisstdoutetstderr. Pour les rediriger vers des fichiers différents, nous pouvons utiliser l'instruction suivante :
command 1>out.txt 2>err.txt
Pour raccourcir un peu l'instruction, nous pouvons omettre "1", car par défautstdoutsera redirigé :
command >out.txt 2>err.txt
Eh bien, essayons de faire quelque chose de "mal." Utilisons la commande rm pour supprimer file1 et folder1 :
[root@localhost ~]# rm -vf folder1 file1 > out.txt 2>err.txt
Maintenant, vérifions les fichiers de sortie suivants :
[root@localhost ~]# cat out.txt removed `file1' [root@localhost ~]# cat err.txt rm: cannot remove `folder1': Is a directory [root@localhost ~]#
Comme nous pouvons le voir, les différents flux ont été séparés dans des fichiers différents. Parfois, ce n'est pas très pratique, car nous voulons voir ce qui s'est passé consécutivement avant ou après une certaine opération lorsqu'une erreur se produit. Pour ce faire, nous pouvons rediriger les deux flux vers le même fichier :
command >>out_err.txt 2>>out_err.txt
Remarque : Veuillez noter que j'ai utilisé ">>" au lieu de ">". Cela nous permet d'ajouter au fichier plutôt que de l'écraser.
Nous pouvons également rediriger un flux vers un autre :
command >out_err.txt 2>&1
Laissez-moi expliquer. La sortie standard de toutes les commandes sera redirigée vers out_err.txt, la sortie d'erreur sera redirigée vers le flux 1 (comme expliqué ci-dessus), et ce flux sera redirigé vers le même fichier. Regardons cet exemple :
[root@localhost ~]# rm -fv folder2 file2 >out_err.txt 2>&1 [root@localhost ~]# cat out_err.txt rm: cannot remove `folder2': Is a directory removed `file2' [root@localhost ~]#
En regardant la sortie combinée, nous pouvons la décrire comme suit : d'abord,rmla commande tente de supprimer folder2, mais elle ne réussira pas car Linux nécessite la-rclé pour permettre à la commandermde supprimer le dossier, tandis que le second file2 sera supprimé. En fournissant lermfournir-v(verbose) clé, nous faisons en sorte que la commande rm nous indique chaque fichier ou dossier supprimé.
C'est presque tout ce que vous devez savoir sur la redirection. Je dis presque, car il existe un autre outil de redirection important, appelé "pipe". En utilisant le symbole | (pipe), nous redirigeons généralement le fluxstdout.
Par exemple, nous avons un fichier texte comme ceci :
[root@localhost ~]# cat text_file.txt This line does not contain H e l l o word This lilne contains Hello This also containd Hello This one no due to HELLO all capital Hello bash world!
Et nous devons trouver les lignes contenant "Hello". Linux dispose d'une commandegrepqui peut faire ce travail :
[root@localhost ~]# grep Hello text_file.txt This lilne contains Hello This also containd Hello Hello bash world! [root@localhost ~]#
Lorsque nous avons un fichier et que nous voulons y effectuer une recherche, cela fonctionne bien. Mais que faire si nous devons trouver quelque chose dans la sortie d'une autre commande ? Oui, bien sûr, nous pouvons rediriger la sortie vers un fichier, puis effectuer la recherche dans le fichier :
[root@localhost ~]# fdisk -l>fdisk.out [root@localhost ~]# grep "Disk /dev" fdisk.out Disk /dev/sda: 8589 MB, 8589934592 bytes Disk /dev/mapper/VolGroup-lv_root: 7205 MB, 7205814272 bytes Disk /dev/mapper/VolGroup-lv_swap: 855 MB, 855638016 bytes [root@localhost ~]#
Et si vous prévoyez d'utiliser grep sur quelque chose entre guillemets doubles et contenant des espaces !
Remarque : La commande fdisk affiche des informations sur les lecteurs de disque sur les systèmes d'exploitation Linux.
Comme nous pouvons le voir, cette méthode est très peu pratique, car nous encombrons rapidement l'espace des fichiers temporaires. Pour accomplir cette tâche, nous pouvons utiliser des pipes. Ils nous permettent de rediriger lestdoutd'une commande vers le fluxstdind'une autre commande :
[root@localhost ~]# fdisk -l | grep "Disk /dev" Disk /dev/sda: 8589 MB, 8589934592 bytes Disk /dev/mapper/VolGroup-lv_root: 7205 MB, 7205814272 bytes Disk /dev/mapper/VolGroup-lv_swap: 855 MB, 855638016 bytes [root@localhost ~]#
Comme vous pouvez le voir, nous obtenons le même résultat sans avoir besoin de fichiers temporaires. Nous redirigeons lefdisk stdoutredirigé versgrep stdin。
Remarque: La redirection par pipe se fait toujours de gauche à droite.
Il existe quelques autres types de redirection, mais nous les aborderons plus tard.
Afficher des informations personnalisées dans le shell
Comme nous le savons, la communication avec le shell et au sein du shell se fait généralement de manière conversationnelle. Alors, créons quelques vrais scripts qui nous parleront aussi. Cela vous apprendra quelques commandes simples et vous donnera une meilleure compréhension du concept des scripts.
Supposons que nous soyons le responsable du service d'assistance d'une entreprise, et que nous voulions créer un script shell pour enregistrer les informations d'appel : numéro de téléphone, nom d'utilisateur et brève description du problème. Nous prévoyons de stocker ces informations dans un fichier texte brut, data.txt, pour des statistiques ultérieures. Le script lui-même fonctionne de manière conversationnelle, ce qui facilitera un peu la vie du personnel du service d'assistance. Donc, d'abord, nous devons afficher des invites. Pour afficher des informations, nous pouvons utiliser les commandes echo et printf. Les deux servent à afficher des informations, mais printf est plus puissant car nous pouvons formater la sortie joliment avec lui ; nous pouvons la rendre alignée à droite, alignée à gauche, ou laisser un espace dédié pour les informations. Commençons par un exemple simple. Pour créer un fichier, utilisez votre éditeur de texte habituel (kate, nano, vi, ...), et créez un fichier nommé note.sh, et écrivez ces commandes dedans :
echo "Phone number ?"
Comment exécuter/lancer un script ?
Après avoir enregistré le fichier, nous pouvons l'exécuter avec la commande bash, en passant notre fichier comme argument :
[root@localhost ~]# bash note.sh Phone number ?
En fait, exécuter le script de cette manière est assez peu pratique. Si nous n'utilisons pas la commandebashcomme préfixe, il serait plus confortable de l'exécuter. Pour rendre le script exécutable, nous pouvons utiliser la commandechmod:
[root@localhost ~]# ls -la note.sh -rw-r--r--. 1 root root 22 Apr 23 20:52 note.sh [root@localhost ~]# chmod +x note.sh [root@localhost ~]# ls -la note.sh -rwxr-xr-x. 1 root root 22 Apr 23 20:52 note.sh [root@localhost ~]#

Remarque: La commande ls affiche les fichiers du dossier courant. En ajoutant l'option -la, elle affiche plus d'informations sur les fichiers.
Comme nous pouvons le voir, avant que la commandechmodne soit exécutée, le script n'a que les permissions de lecture (r) et d'écriture (w). Après avoir exécutéchmod +xil obtient la permission d'exécution (x). (Je couvrirai plus de détails sur les permissions dans le prochain article.) Maintenant, nous l'exécutons simplement comme ceci :
[root@localhost ~]# ./note.sh Phone number ?
Avant le nom du script, j'ai ajouté la combinaison ./ . Le . (point) dans le monde Unix signifie l'emplacement actuel (dossier actuel), et / (barre oblique) est le séparateur de dossiers. (Dans les systèmes Windows, nous utilisons la barre oblique inverse \ pour indiquer la même fonction) Donc, toute cette combinaison signifie : « Exécuter le script note.sh depuis le dossier actuel ». Je pense que ce serait plus clair si j'exécutais ce script avec le chemin complet :
[root@localhost ~]# /root/note.sh Phone number ? [root@localhost ~]#
Cela fonctionne aussi.
Si tous les utilisateurs Linux avaient le même shell par défaut, tout irait bien. Si nous exécutons simplement le script, le shell utilisateur par défaut sera utilisé pour analyser le contenu du script et exécuter les commandes. Les différents shells ont de légères différences de syntaxe, de commandes internes, etc. Donc, pour garantir que notre script utilisebash, nous devons ajouter#!/bin/bashà la première ligne du fichier. Ainsi, le shell utilisateur par défaut invoquera/bin/bash, et ce n'est qu'alors que les commandes du script seront exécutées :
[root@localhost ~]# cat note.sh #!/bin/bash echo "Phone number ?"
Ce n'est que maintenant que nous sommes sûrs à 100 %bashqu'il sera utilisé pour analyser le contenu de notre script. Continuons.
Lecture de l'entrée
Après avoir affiché le message, le script attend la réponse de l'utilisateur. Il y a une commandereadutilisée pour recevoir la réponse de l'utilisateur :
#!/bin/bash echo "Phone number ?" read phone
Après exécution, le script attend la saisie de l'utilisateur jusqu'à ce que l'utilisateur appuie sur [ENTER] pour terminer la saisie :
[root@localhost ~]# ./note.sh Phone number ? 12345 <--- 这儿是我输入的内容 [root@localhost ~]#
Tout ce que vous saisissez sera stocké dans la variablephone; pour afficher la valeur de la variable, nous pouvons aussi utiliser la commandeecho:
[root@localhost ~]# cat note.sh #!/bin/bash echo "Phone number ?" read phone echo "You have entered $phone as a phone number" [root@localhost ~]# ./note.sh Phone number ? 123456 You have entered 123456 as a phone number [root@localhost ~]#
Dansbash, en shell, généralement nous utilisons le signe$(dollar) pour indiquer qu'il s'agit d'une variable, sauf lors de la lecture dans une variable et dans quelques autres rares cas où nous n'utilisons pas ce $ (expliqué plus tard).
Très bien, maintenant nous sommes prêts à ajouter les questions restantes :
#!/bin/bash echo "Phone number?" read phone echo "Name?" read name echo "Issue?" read issue [root@localhost ~]# ./note.sh Phone number? 123 Name? Jim Issue? script is not working. [root@localhost ~]#
Utilisation de la redirection de flux
Parfait ! Il ne reste plus qu'à tout rediriger vers le fichier data.txt. Comme séparateur de champs, nous utiliserons le symbole / (barre oblique).
Remarque: Vous pouvez choisir n'importe quel délimiteur que vous jugez le meilleur, mais assurez-vous que le contenu du fichier ne contient pas ces symboles, sinon cela créera des champs supplémentaires dans les lignes de texte.
N'oubliez pas d'utiliser « >> » au lieu de « > », car nous voulons ajouter la sortie à la fin du fichier !
[root@localhost ~]# tail -2 note.sh read issue echo "$phone/$name/$issue">>data.txt [root@localhost ~]# ./note.sh Phone number? 987 Name? Jimmy Issue? Keybord issue. [root@localhost ~]# cat data.txt 987/Jimmy/Keybord issue. [root@localhost ~]#
Remarque : la commande : tailaffiche les dernièresnlignes.
C'est fait. Exécutons-le à nouveau pour voir :
[root@localhost ~]# ./note.sh Phone number? 556 Name? Janine Issue? Mouse was broken. [root@localhost ~]# cat data.txt 987/Jimmy/Keybord issue. 556/Janine/Mouse was broken. [root@localhost ~]#
Notre fichier grossit. Ajoutons une date au début de chaque ligne. Cela sera utile lorsque nous manipulerons ces statistiques plus tard. Pour ce faire, nous pouvons utiliser la commande date et spécifier un certain format, car je n'aime pas le format par défaut :
[root@localhost ~]# date Thu Apr 23 21:33:14 EEST 2015 <---- date命令的默认输出 [root@localhost ~]# date "+%Y.%m.%d %H:%M:%S" 2015.04.23 21:33:18 <---- 格式化后的输出
Il existe plusieurs façons de lire la sortie d'une commande dans une variable ; dans ce cas simple, nous utiliserons ` (accent grave, pas une apostrophe, sur la même touche que le tilde ~) :
[root@localhost ~]# cat note.sh #!/bin/bash now=`date "+%Y.%m.%d %H:%M:%S"` echo "Phone number?" read phone echo "Name?" read name echo "Issue?" read issue echo "$now/$phone/$name/$issue">>data.txt [root@localhost ~]# ./note.sh Phone number? 123 Name? Jim Issue? Script hanging. [root@localhost ~]# cat data.txt 2015.04.23 21:38:56/123/Jim/Script hanging. [root@localhost ~]#
Hmm... notre script est un peu laid. Embellissons-le. Si vous voulez lire manuellement la commanderead, vous découvrirez que la commande read peut aussi afficher des informations. Pour ce faire, nous devrions utiliser l'option -p ainsi que le message :
[root@localhost ~]# cat note.sh #!/bin/bash now=`date "+%Y.%m.%d %H:%M:%S"` read -p "Phone number: " phone read -p "Name: " name read -p "Issue: " issue echo "$now/$phone/$name/$issue">>data.txt
Vous pouvez directement trouver beaucoup d'informations intéressantes sur diverses commandes depuis la console, tapez simplement :man read, man echo, man date, man ……
D'accord ? C'est beaucoup plus confortable !
[root@localhost ~]# ./note.sh Phone number: 321 Name: Susane Issue: Mouse was stolen [root@localhost ~]# cat data.txt 2015.04.23 21:38:56/123/Jim/Script hanging. 2015.04.23 21:43:50/321/Susane/Mouse was stolen [root@localhost ~]#
Le curseur est après le message (pas sur une nouvelle ligne), ce qui est intéressant. (Note du traducteur LCTT : si vous utilisez la commande echo pour l'affichage, vous pouvez utiliser l'option -n pour éviter un retour à la ligne.)
Boucles
Il est temps d'améliorer notre script. Si l'utilisateur répond à des appels téléphoniques toute la journée, ne serait-ce pas pénible de devoir l'exécuter à chaque fois ? Faisons en sorte que ces activités tournent en boucle indéfiniment :
[root@localhost ~]# cat note.sh
#!/bin/bash
while true
do
read -p "Phone number: " phone
now=`date "+%Y.%m.%d %H:%M:%S"`
read -p "Name: " name
read -p "Issue: " issue
echo "$now/$phone/$name/$issue">>data.txt
done
J'ai échangé les positions des lignesread phoneetnow=date. C'est parce que je veux obtenir l'heure après avoir saisi le numéro de téléphone. Si je le mets à la première ligne de la boucle, alors après une boucle, la variable now obtiendrait l'heure immédiatement après que les données ont été enregistrées dans le fichier. Et ce n'est pas bon, car le prochain appel pourrait être 20 minutes plus tard, ou même plus tard.
[root@localhost ~]# ./note.sh Phone number: 123 Name: Jim Issue: Script still not works. Phone number: 777 Name: Daniel Issue: I broke my monitor Phone number: ^C [root@localhost ~]# cat data.txt 2015.04.23 21:38:56/123/Jim/Script hanging. 2015.04.23 21:43:50/321/Susane/Mouse was stolen 2015.04.23 21:47:55/123/Jim/Script still not works. 2015.04.23 21:48:16/777/Daniel/I broke my monitor [root@localhost ~]#
Remarque : Pour quitter la boucle infinie, vous pouvez appuyer sur [Ctrl]+[C]. Le shell affichera ^ pour représenter la touche CTRL.。
Utilisation de la redirection par tube
Ajoutons plus de fonctionnalités à notre "Frankenstein". Je veux que le script affiche quelques statistiques après chaque appel. Par exemple, je veux voir combien de fois chaque numéro m'a appelé. Pour cela, nous devrions exécuter cat sur le fichier data.txt :
[root@localhost ~]# cat data.txt 2015.04.23 21:38:56/123/Jim/Script hanging. 2015.04.23 21:43:50/321/Susane/Mouse was stolen 2015.04.23 21:47:55/123/Jim/Script still not works. 2015.04.23 21:48:16/777/Daniel/I broke my monitor 2015.04.23 22:02:14/123/Jimmy/New script also not working!!! [root@localhost ~]#
Maintenant, nous pouvons rediriger toute la sortie vers la commandecut, ce qui permet àcutde découper chaque ligne en morceaux (nous utilisons le délimiteur "/"), puis d'afficher le deuxième champ :
[root@localhost ~]# cat data.txt | cut -d"/" -f2 123 321 123 777 123 [root@localhost ~]#
Maintenant, nous pouvons rediriger cette sortie vers une autre commandesort:
[root@localhost ~]# cat data.txt | cut -d"/" -f2|sort 123 123 123 321 777 [root@localhost ~]#
Ensuite, seules les lignes uniques restent. Pour compter les entrées uniques, ajoutez simplement-cl'option à la commandeuniq:
[root@localhost ~]# cat data.txt | cut -d"/" -f2 | sort | uniq -c
3 123
1 321
1 777
[root@localhost ~]#
Ajoutez simplement ceci à la fin de notre boucle :
#!/bin/bash
while true
do
read -p "Phone number: " phone
now=`date "+%Y.%m.%d %H:%M:%S"`
read -p "Name: " name
read -p "Issue: " issue
echo "$now/$phone/$name/$issue">>data.txt
echo "===== We got calls from ====="
cat data.txt | cut -d"/" -f2 | sort | uniq -c
echo "--------------------------------"
done
Exécutez :
[root@localhost ~]# ./note.sh
Phone number: 454
Name: Malini
Issue: Windows license expired.
===== We got calls from =====
3 123
1 321
1 454
1 777
--------------------------------
Phone number: ^C

Le scénario actuel passe par plusieurs étapes bien connues :
- Afficher le message
- Obtenir la saisie de l'utilisateur
- Enregistrer la valeur dans le fichier
- Traiter les données stockées
Mais, si l'utilisateur est un peu responsable, il doit parfois saisir des données, parfois a besoin de statistiques, ou peut-être veut-il rechercher quelque chose dans les données stockées ? Pour ces choses, nous devons utiliser des switch/case et savoir formater joliment la sortie. Cela est utile lorsqu'on « dessine » des tableaux dans le shell.
Source : http://linux.cn/article-5591-1.html