31 votes

Comportement étrange de argv lors du passage d'une chaîne contenant " !!!!".

J'ai écrit un petit programme qui prend des paramètres d'entrée à partir de *argv[] et les imprime. Dans presque tous les cas d'utilisation, mon code fonctionne parfaitement bien. Le problème ne survient que lorsque j'utilise plus d'un point d'exclamation à la fin de la chaîne de caractères que je veux passer comme argument ...

Ça marche :

./program -m "Hello, world!"

Cela ne fonctionne pas :

./program -m "Hello, world!!!!"

^^ Si je fais cela, la sortie du programme est soit deux fois cette chaîne, soit la commande que j'ai entrée avant ./programme.

Cependant, ce que je ne comprends absolument pas : Ce qui suit, bizarrement, fonctionne :

./program -m 'Hello, world!!!!'

^^ La sortie est exactement ...

Hello, world!!!!

... comme souhaité.

Donc, mes questions sont :

  • Pourquoi ce comportement étrange se produit-il lorsque l'on utilise plusieurs points d'exclamation dans une chaîne de caractères ?
  • Pour autant que je sache, en C, on utilise "" pour cordes et '' pour les caractères uniques. Alors pourquoi est-ce que j'obtiens le résultat souhaité en utilisant '' mais pas en utilisant "" comme je le devrais (selon ma compréhension) ?
  • Y a-t-il une erreur dans mon code ou que dois-je changer pour pouvoir entrer n'importe quelle chaîne de caractères (peu importe si, quoi et combien de signes de ponctuation sont utilisés) et obtenir exactement cette chaîne de caractères imprimée ?

Les parties pertinentes de mon code :

// this is a simplified example that, in essence, does the same 
// as my (significantly longer) code
int main(int argc, char* argv[]) {
    char *msg = (char *)calloc(1024, sizeof(char));

    printf("%s", strcat(msg, argv[2])); // argv[1] is "-m"

    free(msg);
}

J'ai déjà essayé de copier le contenu de argv[2] en un char* d'abord et ajouter un '\0' ce qui n'a rien changé.

2 votes

Pourquoi printf("%s", strcat(msg, argv[2])) au lieu de printf("%s", argv[2])) ? ?

2 votes

L'espace, vous devez y échapper. Aussi, calloc() ça n'a pas beaucoup de sens. Juste char msg[1024] est très bon. Lorsque vous utilisez les qoutes simples, la chaîne est transmise telle quelle. Cela n'a rien à voir avec argv ou le c le langage de programmation, mais avec le shell

1 votes

@Michael Walz : Parce que je crée une chaîne beaucoup plus longue dans msg. L'ajout du contenu de argv à celui-ci n'est que la première des nombreuses étapes de mon code complet. Désolé de ne pas avoir clarifié cela plus tôt.

68voto

dbush Points 8590

Ceci n'est pas lié à votre code mais au shell qui le lance.

Dans la plupart des coquillages, !! est un raccourci pour la dernière commande qui a été exécutée. Lorsque vous utilisez des guillemets doubles, l'interpréteur de commandes permet de expansion de l'histoire (ainsi que la substitution de variables, etc.) à l'intérieur de la chaîne, de sorte que lorsque vous mettez !! à l'intérieur d'une chaîne entre guillemets, il substitue la dernière commande exécutée.

Ce que cela signifie pour votre programme est que tout cela se passe avant votre programme est exécuté, donc il n'y a pas grand chose que le programme puisse faire à part vérifier si la chaîne de caractères qui est passée est valide.

En revanche, lorsque vous utilisez des guillemets simples, le shell fait no ne fait aucune substitution et la chaîne est passée au programme sans être modifiée.

Vous devez donc utiliser des guillemets simples pour passer cette chaîne. Vos utilisateurs doivent le savoir s'ils ne veulent pas que la substitution se produise. L'alternative est de créer un shell wrapper script qui demande à l'utilisateur la chaîne à passer, puis le script appellerait ensuite votre programme avec les arguments appropriés.

0 votes

Je vois, merci ! Il me reste une question à poser : Existe-t-il un moyen de faire en sorte que les utilisateurs de mon programme puissent toujours utiliser "Something !!!!" comme paramètre d'entrée sans subir ce comportement ? (quel que soit le shell qu'ils utilisent)

13 votes

@ci7i2en4 C'est à vos utilisateurs de le découvrir. Peut-être veulent-ils que cette expansion se produise, peut-être pas.

2 votes

@ci7i2en4, dans Bash, set +o histexpand ou set +H pour désactiver l'expansion de l'historique. D'autres shells peuvent avoir d'autres paramètres.

9voto

Joachim Pileborg Points 121221

L'interpréteur de commandes effectue l'expansion dans les chaînes de caractères doublement citées. Et si vous lisez la page du manuel Bash (en supposant que vous utilisez Bash, ce qui est le cas par défaut sur la plupart des distributions Linux), alors si vous regardez à la section Expansion de l'histoire vous verrez que !! signifie

Reportez-vous à la commande précédente.

Así que !!!! dans votre chaîne de caractères entre guillemets s'étendra à la commande précédente, deux fois.

Une telle expansion n'est pas faite pour les chaînes de caractères à guillemets simples.

Le problème n'est donc pas dans votre programme, il est dû à l'environnement (le shell) qui appelle votre programme.

8voto

UncleCarl Points 327

En plus des réponses fournies, vous devez vous rappeler que echo est votre ami shell. Si vous préfixez votre commande par "echo ", vous verrez ce que le shell envoie réellement à votre script.

echo ./program -m "Hello, world!!!!"

Cela vous aurait montré une certaine étrangeté et aurait pu vous orienter dans la bonne direction.

1 votes

echo est en fait un très mauvais choix d'outils pour cette utilisation -- echo "hello world" y echo "hello" "world" ont exactement le même résultat, après tout, bien qu'il s'agisse de commandes très différentes.

1 votes

Considérez plutôt : print_args() { printf '%q ' "$@"; printf '\n'; } -- par la suite, print_args ./program -m "Hello, world!!!!" émettra des arguments d'une manière qui rendra leur interprétation non ambiguë même dans les cas où echo se trompe.

0 votes

Ce n'est que l'opinion de Charles. J'apprécie qu'il montre une déficience dans l'utilisation d'echo comme outil, particulièrement bien qu'il ait fourni une alternative. Pour moi, c'est un mauvais choix de compter sur une fonction qui se trouve dans chaque environnement Linux que je visite (ou que je copie/pâte) et de me souvenir du nom de cette fonction. Le lecteur a maintenant deux mauvais choix. L'un dans une réponse et l'autre dans un commentaire.

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