Résoudre un exercice en shell, je bloque :s
Bonjour,
J'ai trouvé un exercice type partiel sur lequel je bloque, j'ai du mal avec les while read, redirection le tout introduit dans une boucle for *-*
L'exercice :
Exercice 1 : Le script mon_grep
Le programme grep 1. Sous sa forme la plus rudimentaire, cette commande recherche une chaîne de caractère dans un ou plusieurs fichiers et affiche la ou les lignes correspondantes. Dans l’exemple suivant, je recherche mon
nom d’utilisateur (pierre) dans les fichiers de configuration
Le but est ici d’écrire votre propre version (appelée
1. Écrire un script shell
— le fichier correspondant ;
— le numéro de ligne correspondant.
Pour ce faire, vous utiliserez une boucle for pour parcourir les arguments de la ligne de commande (les fichiers à lire) ainsi qu’une construction
2. Faire en sorte que, si au moins une occurrence de la chaîne est trouvée, le statut de sortie soit
3. Écrire dans le script une fonction usage qui décrit très brièvement à l’utilisateur le fonctionnement du script et appeler cette fonction dans les cas suivants :
— le premier argument est
— le premier argument n’est ni
//////////////////////////////////////////////////////////////////////////////////////////
De ce que j'ai compris, ça doit ressembler à : (j'ai pris 3 fichiers txt quelconque) :
J'ai trouvé un exercice type partiel sur lequel je bloque, j'ai du mal avec les while read, redirection le tout introduit dans une boucle for *-*
L'exercice :
Exercice 1 : Le script mon_grep
Le programme grep 1. Sous sa forme la plus rudimentaire, cette commande recherche une chaîne de caractère dans un ou plusieurs fichiers et affiche la ou les lignes correspondantes. Dans l’exemple suivant, je recherche mon
nom d’utilisateur (pierre) dans les fichiers de configuration
/etc/passwd,
/etc/groupet
/etc/hostname:
$ grep pierre /etc/group /etc/passwd /etc/hostname
/etc/group:wheel:x:10:pierre
/etc/group:pierre:x:1000:
/etc/group:plugdev:x:1001:pierre
/etc/passwd:pierre:x:1000:1000:pierre:/home/pierre:/bin/bash
Le but est ici d’écrire votre propre version (appelée
mon_grep) de
grep, évidemment sans utiliser
grep(ni
sed, ni
awk, ...).
1. Écrire un script shell
mon_grepqui recherche une chaîne (son premier argument) dans un ou plusieurs fichiers (les arguments suivants). Les lignes qui contiennent la chaîne sont affichées avec en début de ligne :
— le fichier correspondant ;
— le numéro de ligne correspondant.
Pour ce faire, vous utiliserez une boucle for pour parcourir les arguments de la ligne de commande (les fichiers à lire) ainsi qu’une construction
while readavec une redirection pour lire chaque fichier ligne par ligne.
2. Faire en sorte que, si au moins une occurrence de la chaîne est trouvée, le statut de sortie soit
0(succès) sinon, si la chaîne n’est pas trouvée, il vaut
1(ceci afin de pouvoir utiliser
mon_grepdans une construction if par exemple).
3. Écrire dans le script une fonction usage qui décrit très brièvement à l’utilisateur le fonctionnement du script et appeler cette fonction dans les cas suivants :
— le premier argument est
--helpou
-h: dans ce cas, le script se termine immédiatement et normalement, avec un statut de sortie
0;
— le premier argument n’est ni
--helpni
-het le nombre d’arguments est inférieur à 2 : dans ce cas l’utilisateur n’a pas compris comment utiliser ce script, on lui rappelle le mode d’emploi et le script se termine avec un statut de sortie 2 pour indiquer un problème.
//////////////////////////////////////////////////////////////////////////////////////////
De ce que j'ai compris, ça doit ressembler à : (j'ai pris 3 fichiers txt quelconque) :
$ cat pirouette.txt
Il était un petit homme,
Pirouette, cacaouète.
Il était un petit homme,
Qui avait une drôle de maison,
Qui avait une drôle de maison.
$ cat poissons.txt
Les petits poissons dans l'eau
Nagent, nagent, nagent, nagent.
Les petits poissons dans l'eau
Nagent aussi bien que les gros.
$ cat escargot.txt
Petit escargot porte sur son dos,
Sa maisonnette.
Aussitôt qu'il pleut,
Il est tout heureux,
Il sort sa tête.
$ ./mon_grep etit pirouette.txt poissons.txt escargot.txt
Ligne 1 de pirouette.txt: Il était un petit homme,
Ligne 3 de pirouette.txt: Il était un petit homme,
Ligne 1 de poissons.txt: Les petits poissons dans l'eau
Ligne 3 de poissons.txt: Les petits poissons dans l'eau
Ligne 1 de escargot.txt: Petit escargot porte sur son dos,
$ ./mon_grep maison pirouette.txt poissons.txt escargot.txt
Ligne 4 de pirouette.txt: Qui avait une drôle de maison,
Ligne 5 de pirouette.txt: Qui avait une drôle de maison.
Ligne 2 de escargot.txt: Sa maisonnette.
$ ./mon_grep 'physique nucléaire' pirouette.txt poissons.txt escargot.txt
$ echo $?
2
$ ./mon_grep lol
Usage: ./mon_grep chaine fichier...
ou: --help|-h
Affiche les lignes de fichier... contenant chaine avec leur numéro
Ou bien cette aide avec l'argument --help
$ ./mon_grep -h
Usage: ./mon_grep chaine fichier...
ou: --help|-h
Affiche les lignes de fichier... contenant chaine avec leur numéro
Ou bien cette aide avec l'argument --help
$ echo $?
0
A voir également:
- Résoudre un exercice en shell, je bloque :s
- Classic shell - Télécharger - Personnalisation
- Code puk bloqué - Guide
- Téléphone bloqué code verrouillage - Guide
- Pavé tactile bloqué - Guide
- Compte gmail bloqué - Guide
4 réponses
Bonjour,
Mon premier conseil serait de soigner l'indentation pour que ce soit plus lisible. Pense aussi à utiliser les balises de code (4e bouton au dessus de la boîte dans laquelle tu tapes tes messages) pour rendre tes messages plus lisibles.
Il y a d'autres choses qui ne vont pas, qu'on observe quand on lance ton script :
Après ces corrections ça donne :
Mon premier conseil serait de soigner l'indentation pour que ce soit plus lisible. Pense aussi à utiliser les balises de code (4e bouton au dessus de la boîte dans laquelle tu tapes tes messages) pour rendre tes messages plus lisibles.
Il y a d'autres choses qui ne vont pas, qu'on observe quand on lance ton script :
- Il y a un $ en trop devant
echo
- Pour quitter le programme il faut utiliser le mot clé
exit
et pasreturn
- Le code de retour du programme n'est pas toujours précisé (si on rentre dans le
elif
). En pratique, le programme retournera implicitement 0. - Le
case
est un peu bizarre. Il y a des manières plus élégantes en shell de vérifier si une chaîne est inclue dans une autre (voir cette discussion) - Il est fortement recommandé d'encadrer les évaluations de variables par des guillements (afin de traiter correctement les chaînes de caractères contenant des espaces).
- Il est recommandé de mettre en première ligne un shebang qui indique l'interpréteur à utiliser (en particulier, si tu utilises des primitives bash, c'est là qu'il faut indiquer
bash
au lieu desh
).
Après ces corrections ça donne :
#!/bin/sh usage (){ echo "Usage: ./mon_grep chaine fichier... ou: --help|-h Affiche les lignes de fichier... contenant chaine avec leur numéro Ou bien cette aide avec l'argument --help" } ret=0 if [ $# -ne 0 ] && { [ "$1" = --help ] || [ "$1" = -h ]; }; then usage elif [ $# -gt 1 ]; then for f in $@ do cpt=0 if [ "$1" != "$f" ]; then while read ligne do cpt=$(( $cpt + 1 )) if [[ "$line" =~ "$1" ]] then echo "Ligne $cpt de $f : $ligne" fi done < $f fi done ret=0 else echo "erreur: trop d'arguments" usage ret=2 fi exit ret
Bonjour,
Je t'invite à faire des questions concises et précises quand tu fais appel à un forum, cela augmentera les chances que tu aies une réponse.
J'ai essayé plusieurs fois, mais le
Ce lien te montre comment réaliser une telle construction.
Bonne chance
Je t'invite à faire des questions concises et précises quand tu fais appel à un forum, cela augmentera les chances que tu aies une réponse.
J'ai essayé plusieurs fois, mais le
while read+ redirection me bloque
Ce lien te montre comment réaliser une telle construction.
Bonne chance
Bonjour,
Merci, j'ai avancé, mais j'ai 2-3 questions car je bloque sur certains points, mon script :
Le problème, tout marche mais lorsque je fais (je ne tombe par sur 2 lorsque j'exécute la commande echo $2 et je ne sais pas comment résoudre ce problème) :
Il s'agit du cas où il y'a trop d'arguments, et je dois finir avec un statut de sortie 2 (voir consigne).
\Si tu pouvais m'aider sur ça, ou quelqu'un d'autres, merci beaucoup !
Bonne journée
Merci, j'ai avancé, mais j'ai 2-3 questions car je bloque sur certains points, mon script :
usage (){ echo "Usage: ./mon_grep chaine fichier... ou: --help|-h Affiche les lignes de fichier... contenant chaine avec leur numéro Ou bien cette aide avec l'argument --help" } if [ $# -ne 0 ] && { [ $1 = --help ] || [ $1 = -h ]; }; then usage return 0 elif [ $# -gt 1 ]; then for f in $@ do cpt=0 if [ $1 != $f ]; then while read ligne do cpt=$(( $cpt + 1 )) case $ligne in *$1*) echo "Ligne $cpt de $f : $ligne" ;; esac done < $f fi done else echo "erreur: trop d'arguments" usage return 2 fi
Le problème, tout marche mais lorsque je fais (je ne tombe par sur 2 lorsque j'exécute la commande echo $2 et je ne sais pas comment résoudre ce problème) :
$ ./mon_grep 'physique nucléaire' pirouette.txt poissons.txt escargot.txt
$ echo $?
2
Il s'agit du cas où il y'a trop d'arguments, et je dois finir avec un statut de sortie 2 (voir consigne).
\Si tu pouvais m'aider sur ça, ou quelqu'un d'autres, merci beaucoup !
Bonne journée
Merci, j'ai essayé le code mais il y'a un problème, lorsque je tape la commande suivante :
Je tombe sur une erreur :
Je crois que c'est par rapport au
Et en essayant de modifier j'ai fais ça mais le problème c'est que j'ai certes le
Le programme : (j'ai juste remplacé la ligne 21 par ce qui y était avant)
Merci pour l'aide !
./mon_grep etit pirouette.txt poissons.txt escargot.txt
Je tombe sur une erreur :
./ttt: 21: ./ttt: [[: not found
./ttt: 21: ./ttt: [[: not found
./ttt: 21: ./ttt: [[: not found
./ttt: 21: ./ttt: [[: not found
./ttt: 21: ./ttt: [[: not found
./ttt: 21: ./ttt: [[: not found
./ttt: 21: ./ttt: [[: not found
./ttt: 21: ./ttt: [[: not found
./ttt: 34: exit: Illegal number: ret
Je crois que c'est par rapport au
=~, d'ailleurs je n'ai jamais vu cette combinaison.
Et en essayant de modifier j'ai fais ça mais le problème c'est que j'ai certes le
echo $?--> 2 mais si je fais
./mon_grep -hpuis
echo $?, je suis censé avoir 0 mais non je tombe à chaque fois sur 2.
Le programme : (j'ai juste remplacé la ligne 21 par ce qui y était avant)
usage (){ echo "Usage: ./mon_grep chaine fichier... ou: --help|-h Affiche les lignes de fichier... contenant chaine avec leur numéro Ou bien cette aide avec l'argument --help" } ret=0 if [ $# -ne 0 ] && { [ $1 = --help ] || [ $1 = -h ]; }; then usage return 0 elif [ $# -gt 1 ]; then for f in $@ do cpt=0 if [ $1 != $f ]; then while read ligne do cpt=$(( $cpt + 1 )) case $ligne in *$1*) echo "Ligne $cpt de $f : $ligne" ;; esac done < $f fi done ret=0else echo "erreur: trop d'arguments" usage ret=2 fiexit ret
Merci pour l'aide !
Bonjour,
Tu as cette erreur car ton script est lancé par
Exemple : Soit le fichier
Si tu le lances via
Si tu le lances via
Si tu rends le fichier exécutable, ton shell examinera le shebang (ici
Bonne chance
Tu as cette erreur car ton script est lancé par
shet non par
bash, or
=~est un opérateur bash, comme expliqué ici. L'intérêt du shebang est précisément que le script soit lancé avec le bon interpréteur shell.
Exemple : Soit le fichier
exemple.sh
#!/bin/bash phrase="ceci est une phrase" word="une" if [[ "$phrase" =~ "$word" ]] then echo "'$word' trouvé" else echo "'$word' pas trouvé" fi
Si tu le lances via
sh, tu as une erreur :
(mando@silk) (~) $ sh exemple.sh
toto.sh: 6: [[: not found
'une' pas trouvé
Si tu le lances via
bash, tu n'as pas d'erreur :
(mando@silk) (~) $ bash exemple.sh
'une' trouvé
Si tu rends le fichier exécutable, ton shell examinera le shebang (ici
#!/bin/bash) pour déterminer quel interpréteur shell utiliser :
(mando@silk) (~) $ chmod a+x exemple.sh
(mando@silk) (~) $ ./exemple.sh
'une' trouvé
Bonne chance