245 votes

Existe-t-il une instruction "goto" en bash ?

Existe-t-il une instruction "goto" en bash ? Je sais que c'est considéré comme une mauvaise pratique, mais j'ai besoin d'un "goto" spécifique.

5 votes

Non, il n'y a pas goto dans bash (au moins il est dit command not found pour moi). Pourquoi ? Il y a de fortes chances qu'il y ait un meilleur moyen de le faire.

4 votes

Vous n'avez jamais besoin de goto, au pire vous avez besoin de plus de pratique avec d'autres outils de flux de contrôle.

171 votes

Il a peut-être ses raisons. J'ai trouvé cette question parce que je veux un goto pour passer au-dessus d'une grande quantité de code afin de déboguer un grand script sans attendre une heure pour que diverses tâches non liées se terminent. Je n'utiliserais certainement pas un goto dans le code de production, mais pour le débogage de mon code, cela me faciliterait infiniment la vie, et il serait plus facile de le repérer quand il faudrait le supprimer.

141voto

Michael Rusch Points 490

Si vous l'utilisez pour sauter une partie d'un grand script pour le débogage (voir le commentaire de Karl Nicoll), alors if false pourrait être une bonne option (pas sûr que "false" soit toujours disponible, pour moi c'est dans /bin/false) :

# ... Code I want to run here ...

if false; then

# ... Code I want to skip here ...

fi

# ... I want to resume here ...

La difficulté survient lorsqu'il s'agit d'arracher votre code de débogage. La construction "if false" est assez simple et mémorable, mais comment trouver le fi correspondant ? Si votre éditeur vous permet d'indenter un bloc, vous pourriez indenter le bloc sauté (vous voudrez alors le remettre en place lorsque vous aurez terminé). Ou un commentaire sur la ligne fi, mais il faudrait que ce soit quelque chose dont vous vous souvenez, ce qui, je le soupçonne, dépendra beaucoup du programmeur.

6 votes

Oui false est toujours disponible. Mais si vous avez un bloc de code que vous ne voulez pas exécuter, il suffit de le commenter. Ou bien supprimez-le (et regardez dans votre système de contrôle de la source si vous avez besoin de le récupérer plus tard).

1 votes

Si le bloc de code est trop long pour être commenté fastidieusement une ligne à la fois, voyez ces astuces. stackoverflow.com/questions/947897/ Cependant, ils n'aident pas non plus un éditeur de texte à faire correspondre le début à la fin, et ne constituent donc pas une grande amélioration.

4 votes

"if false" est souvent bien meilleur que la mise en commentaire du code, car il garantit que le code inclus continue d'être un code légal. La meilleure excuse pour commenter du code est lorsqu'il doit vraiment être supprimé, mais qu'il y a quelque chose dont il faut se souvenir - alors c'est juste un commentaire, et non plus du "code".

89voto

ruakh Points 68789

Non, il n'y en a pas ; voir §3.2.4 "Commandes composées" dans le document Manuel de référence Bash pour des informations sur les structures de contrôle qui faire existent. En particulier, notez la mention de break y continue qui ne sont pas aussi flexibles que les goto mais sont plus flexibles dans Bash que dans certains langages, et peuvent vous aider à obtenir ce que vous voulez. (Ce que vous voulez . . .)

16 votes

Pourriez-vous développer l'expression "plus souple en Bash que dans certains langages" ?

26 votes

@user239558 : Certaines langues vous permettent seulement de break o continue à partir de la boucle la plus proche, alors que Bash vous permet de spécifier le nombre de niveaux de boucle à sauter. (Et même des langages qui vous permettent de break o continue à partir de boucles arbitraires, la plupart exigent que cela soit exprimé de manière statique -- par ex, break foo; sortira de la boucle étiquetée foo -- alors qu'en Bash, elle est exprimée de manière dynamique -- par exemple, break "$foo" va sortir de $foo boucles.)

1 votes

Je recommande de définir des fonctions avec les caractéristiques nécessaires et de les appeler à partir d'autres parties du code.

33voto

Paul Brannan Points 550

Vous pouvez utiliser case dans bash pour simuler un goto :

#!/bin/bash

case bar in
  foo)
    echo foo
    ;&

  bar)
    echo bar
    ;&

  *)
    echo star
    ;;
esac

produit :

bar
star

4 votes

Notez que cela nécessite bash v4.0+ . Il ne s'agit toutefois pas d'un outil polyvalent goto mais une option de repli pour le case déclaration.

4 votes

Je pense que cela devrait être la réponse. j'ai un réel besoin de go to afin de supporter la reprise de l'exécution d'un script, à partir d'une instruction donnée. c'est, de toutes les manières, sauf sémantique, goto, et la sémantique et les sucres syntaxiques sont mignons, mais pas strictement nécessaires. excellente solution, IMO.

1 votes

@nathang, que ce soit le site La réponse dépend du fait que votre cas corresponde ou non au sous-ensemble du cas général sur lequel le PO a posé la question. Malheureusement, la question porte sur le cas général, ce qui rend cette réponse trop étroite pour être correcte. (Cette question devrait-elle être fermé comme trop large pour cette raison est une discussion différente).

9voto

Robert Street Points 83

Pour tous ceux qui tombent sur ce fil de discussion et qui lisent toutes les déclarations du genre "goto ne sert à rien". Je peux rapidement citer un exemple extrêmement utile :

Sans goto 1 :

int count = 1000;
char* mystring = malloc(sizeof(char)*count);
bool foundPair = false;
for(int i = 0; i < count - 1;i++)
{
    for(int j = i+1; j < count; j++)
    {
        if(*char[i] == *char[j]) foundPair = true;
            if(foundPair) break;
    }
    if(foundPair) break;
}

Sans goto 2 :

int count = 1000;
char* mystring = malloc(sizeof(char)*count);
bool foundPair = false;
for(int i = 0; i < count - 1 && !foundPair;i++)
{
    for(int j = i+1; j < count && !foundPair; j++)
    {
        if(*char[i] == *char[j]) foundPair = true;
    }
}
i--; j--;

Avec goto :

int count = 1000;
char* mystring = malloc(sizeof(char)*count);
for(int i = 0; i < count - 1;i++)
{
    for(int j = i+1; j < count; j++)
    {
        if(*char[i] == *char[j]) goto foundPair;
    }
}
goto nopair:
foundPair: .....

noPair: ..whatever

Cela supprime 1) la nécessité de créer un booléen en mémoire, 2) une instruction d'affectation et 3) deux instructions de comparaison. Beaucoup de conneries juste pour sortir des boucles internes où un simple saut au niveau de l'assemblage (i.e. goto) est beaucoup plus efficace.

Nous savons tous aussi que, le plus souvent, un programme VA se retrouver avec des boucles for imbriquées pour les recherches, les opérations complexes (comme l'algorithme scrypt), etc. Chacun de ces octets pour une vérification booléenne s'additionne. Chacun de ces octets pour un contrôle booléen s'additionne. Une affectation supplémentaire de la mémoire à un registre peut interférer avec le cache et le pipeline comme un fou et, même si ce n'est pas le cas, l'affectation est toujours lente. Ensuite, les comparaisons supplémentaires ne font qu'ajouter des problèmes de cache et de pipeline. Ajoutez à cela le fait que de nombreux langages de haut niveau comme Java et .Net (C#, F#, etc.) exécutent un code d'octet dans un environnement d'exécution interprété et vous obtenez une surcharge encore plus importante pour chacune de ces opérations qu'au simple niveau de la machine.

La seule autre "alternative" est de placer la boucle for imbriquée dans une fonction à part entière et de renvoyer une valeur :

bool HasPair(char* mystring, int strlen)
{
    for(int i = 0; i < count - 1;i++)
    {
        for(int j = i + 1; j < count; j++)
        {
            if(*char[i] == *char[j]) return true;
        }
    }
    return false;
}

int count = 1000;
char* mystring = malloc(sizeof(char)*count);
if(HasPair(mystring,1000))
{
    //do something
}

Cependant, cela ajoute la surcharge d'un appel de fonction : mise à jour du pointeur de pile, insertion des deux variables et d'autres informations sur la pile, retrait de tout cela de la pile, et enfin retrait de la variable de la pile. Sans compter que si la valeur de retour était plus complexe (notre exemple est relativement simple), nous devrions retourner, par exemple, i et j, si les adresses et non les valeurs sont importantes.

Étant donné que la plupart des langages ne permettent pas de renvoyer plusieurs variables nommées (c'est-à-dire que les variables multiples nécessitent un tableau, une structure ou un objet quelconque), il faut créer un nouveau type de retour, allouer de la mémoire pour ce type, attribuer les valeurs i et j (ou toute autre valeur à attribuer) et enfin renvoyer le pointeur/l'instance du type.

En résumé, les goto sont extrêmement utiles si vous voulez sauter plusieurs opérations inutiles et, en général, c'est le seul moyen de sortir efficacement des boucles imbriquées de type for, while et do sans ajouter une complexité excessive à votre application.

Faire tourner de simples applications de bureau, qui s'en soucie vraiment de nos jours, n'est-ce pas ? Mais, si vous parlez de choses comme les fonctions centrales des noyaux Linux, iOS, Windows, etc., alors l'accélération de ces types de fonctions est extrêmement importante. Ou, pour les personnes qui programment des applications pour les téléphones intelligents, il est important de tirer le maximum de vitesse des petits processeurs et des piles. Il en va de même pour la programmation de jeux. La plupart des opérations mathématiques sur les matrices, etc. finissent par nécessiter de multiples boucles et nous savons tous que nous voulons que chaque bit de puissance de traitement soit utilisé à bon escient afin d'avoir une expérience réaliste dans nos jeux.

-RJS

6voto

Serge Roussak Points 111

Il existe un autre moyen d'obtenir les résultats souhaités : la commande. trap . Il peut être utilisé à des fins de nettoyage par exemple.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X