web-dev-qa-db-fra.com

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

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

Cela marche:

./program -m "Hello, world!"

Cela ne fonctionne pas:

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

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

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

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

^^ La sortie est exactement ...

Hello, world!!!!

... comme vous le souhaitez.

Donc, mes questions sont:

  • Pourquoi ce comportement étrange se produit-il lors de l'utilisation de plusieurs points d'exclamation dans une chaîne?
  • Pour autant que je sache, en C, vous utilisez "" pour les chaînes et '' pour les caractères simples. Alors pourquoi obtenir le résultat souhaité lors de l'utilisation de '', mais pas lors de l'utilisation de "" comme je devrais (dans ma compréhension)?
  • Y a-t-il une erreur dans mon code ou que dois-je changer pour pouvoir entrer une chaîne (peu importe si, quoi et combien de signes de ponctuation sont utilisés) et obtenir exactement cette chaîne 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] dans une char* tampon d'abord et ajout d'un '\0', ce qui n'a rien changé.

31
ci7i2en4

Ce n'est pas lié à votre code mais au shell qui le démarre.

Dans la plupart des coquilles, !! est un raccourci pour la dernière commande exécutée. Lorsque vous utilisez des guillemets doubles, le shell permet expansion de l'historique (avec substitution de variables, etc.) dans la chaîne, donc lorsque vous mettez !! à l'intérieur d'une chaîne entre guillemets, il remplace la dernière exécution de commande.

Ce que cela signifie pour votre programme, c'est que tout cela se produit avant que votre programme soit exécuté, donc il n'y a pas grand-chose que le programme peut faire sauf vérifier si la chaîne qui est transmis est valide.

En revanche, lorsque vous utilisez des guillemets simples, le shell pas effectue des substitutions et la chaîne est passée au programme sans modification.

Vous devez donc utiliser des guillemets simples pour transmettre cette chaîne. Vos utilisateurs auraient besoin de le savoir s'ils ne veulent pas de substitution. L'alternative consiste à créer un script shell wrapper qui invite l'utilisateur à saisir la chaîne, puis le script appellera ensuite votre programme avec les arguments appropriés.

68
dbush

Le Shell fait l'expansion dans les chaînes entre guillemets doubles. Et si vous lisez la page de manuel de Bash (en supposant que vous utilisez Bash, qui est la valeur par défaut sur la plupart des distributions Linux), alors si vous regardez la section Expansion de l'historique vous verrez que !! veux dire

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

Donc !!!! dans votre chaîne entre guillemets doubles se développera à la commande précédente, deux fois.

Une telle expansion n'est pas faite pour les chaînes entre guillemets simples.

Le problème ne vient donc pas de votre programme, il est dû à l'environnement (le Shell) appelant votre programme.

9

En plus des réponses fournies, vous devez vous rappeler que l'écho est votre ami Shell. Si vous préfixez votre commande avec "echo", vous verrez ce que Shell envoie réellement à votre script.

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

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

8
UncleCarl