Résoudre un exercice en shell, je bloque :s

Fermé
Pierre - Modifié le 18 mai 2021 à 16:48
mamiemando Messages postés 33357 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 14 novembre 2024 - 19 mai 2021 à 16:02
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
/etc/passwd
,
/etc/group
et
/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_grep
qui 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 read
avec 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_grep
dans 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
--help
ou
-h
: dans ce cas, le script se termine immédiatement et normalement, avec un statut de sortie
0
;
— le premier argument n’est ni
--help
ni
-h
et 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:

4 réponses

mamiemando Messages postés 33357 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 14 novembre 2024 7 799
18 mai 2021 à 17:58
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 :
  • Il y a un $ en trop devant
    echo
  • Pour quitter le programme il faut utiliser le mot clé
    exit
    et pas
    return
  • 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 de
    sh
    ).


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
2
mamiemando Messages postés 33357 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 14 novembre 2024 7 799
18 mai 2021 à 16:51
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
while read
+ redirection me bloque


Ce lien te montre comment réaliser une telle construction.

Bonne chance
1
Bonjour,

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
0
mamiemando Messages postés 33357 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 14 novembre 2024 7 799 > Pierre
19 mai 2021 à 15:48
Bonjour, tu peux contrôler le nombre d'arguments en évaluant
$#
.
0
J'ai essayé plusieurs fois, mais le
while read
+ redirection me bloque
0
Merci, j'ai essayé le code mais il y'a un problème, lorsque je tape la commande suivante :

./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 -h
puis
 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 !
0
mamiemando Messages postés 33357 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 14 novembre 2024 7 799
19 mai 2021 à 16:02
Bonjour,

Tu as cette erreur car ton script est lancé par
sh
et 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
0