Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1296 connectés 

  FORUM HardWare.fr
  Programmation
  Shell/Batch

  [shell] Passer stdin à une commande passé au shell sur stdin

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

[shell] Passer stdin à une commande passé au shell sur stdin

n°1740223
matafan
Posté le 02-06-2008 à 13:44:58  profilanswer
 

Je voudrait savoir si vous voyez un moyen de passer des données sur l'entrée standard d'une commande, sachant que la commande en question est passée au shell via l'entrée standard du shell. Par exemple considérons la commande suivante :

cat > myfile


Evidemment comma ça elle ne fait pas grand chose, elle est faite pour lire des données sur stdin et les mettre dans un fichier. Elle pourrait par exemple être utilisée comme ça (oui je sais ça parrait bizarre, mais il y a une bonne raison pour laquelle je veux faire ce genre de choses) :

echo "my data" | cat > myfile


Pas de problèmes... Maintenant, imaginer que la commande n'est pas tappée en live dans un shell, ni écrite dans un fichier pour être exécutée, ni passée au shell avec -c, mais qu'elle est passée au shell sur stdin, comme ça :

echo "cat > myfile" | sh


Et là paf, problème : comme le stdin du shell est utilisé pour passer la commande à exécuter, je ne peux plus l'utiliser pour passer ls données qui doivent être consommées par le cat.

 

Donc voilà, je cherche un moyen de passer des données à l'entrée standard d'une commande, quand la commande elle même est passée au shell via l'entrée standard. J'imagine vaguement qu'on peut faire quelque chose en passant des données au shell sur le file descriptor 3 par exemple, et faire en sorte que la commande prennent son entrée standard sur le fd 3... Mais en pratique je ne vois pas trop comment faire.


Message édité par matafan le 02-06-2008 à 13:46:27
mood
Publicité
Posté le 02-06-2008 à 13:44:58  profilanswer
 

n°1740226
Elmoricq
Modérateur
Posté le 02-06-2008 à 13:48:38  profilanswer
 

J'pige pas bien ce que tu essaies de faire en fait.  [:croquignol]

n°1740237
matafan
Posté le 02-06-2008 à 13:58:03  profilanswer
 

C'est ce que je craignais :D Faudra que j'explique exactement ce que je fais.

n°1740418
h4rold
Posté le 02-06-2008 à 16:52:23  profilanswer
 

Tu veux rester en bash ou le C ca te va ?

n°1740424
Taz
bisounours-codeur
Posté le 02-06-2008 à 16:57:35  profilanswer
 

xargs ?


Message édité par Taz le 02-06-2008 à 16:57:57
n°1740644
matafan
Posté le 03-06-2008 à 09:46:19  profilanswer
 

En fait voilà : je fais un programme en C qui doit générer des lignes de commandes à partir de règles qui se trouvent dans un fichier xml, et les faire exécuter au shell. Pour ce faire j'ai décidé de passer la ligne de commande sur l'entrée standard du shell, parce que c'est la solution la plus robuste. Les autres solutions envisageables, mais moins bonnes, sont :
 

  • Passer la ligne de commande au shell avec -c (/usr/bin/ksh -c "ma ligne de commande" ). Ca pose de gros problèmes dans le cas général car si la ligne de commande contient des choses un peu spéciales, genre double quotes, simple quotes, caractères "--", etc... Ca risque de ne pas marcher.
  • Ecrire la commande dans un fichier temporaire, puis demander au shell d'exécuter le fichier (/usr/bin/ksh tmp_file). Mais les fichiers temporaires, ce n'est jamais génial.


Donc, j'ai décidé de passer la ligne de commande sur l'entrée standard du shell : pipe, dup2, fork, exec("/usr/bin/ksh" ), puis j'écris la commande dans le pipe.
 
Seulement voilà, parfois, j'ai besoin d'envoyer des données sur l'entrée standard de ma ligne de commande. Si j'utilisais "ksh -c" ou "ksh tmp_file" je n'aurais pas de problème, il suffirait d'envoyer les données sur l'entrée standard du shell, et elles seraient consommées par la ligne de commande. Mais avec la solution retenues ce n'est pas possible, car c'est le shell lui même qui consomme données de son entrée standard, pour les interpréter.
 
Au final, ma solution bancale c'est de continuer à envoyer ma ligne de commande sur l'entrée standard du shell quand je n'ai pas besoin d'envoyer de données à l'entrée standard de la ligne de commande. Par contre, quand j'ai besoin d'envoyer des données à l'entrée standard de ma ligne de commande, je prendrais la solution du fichier temporaire, et j'enverrais les données sur l'entrée standard du shell.

n°1740645
Taz
bisounours-codeur
Posté le 03-06-2008 à 09:49:25  profilanswer
 

OK mais bon comme tu dis, vu que la solution choisie est bidon, c'est forcément bancale ensuite. Question à 1000PO: t'as vraiment besoin de passer toutes tes commandes dans un shell et dans le même ?

n°1740655
matafan
Posté le 03-06-2008 à 09:57:24  profilanswer
 

Non, chaque ligne de commande est exécutée dans un nouveau shell.

n°1740661
Taz
bisounours-codeur
Posté le 03-06-2008 à 10:01:56  profilanswer
 

bah alors tu t'en fous du shell, il sert à rien, exécute directement la commande.

n°1740677
h4rold
Posté le 03-06-2008 à 10:16:23  profilanswer
 

Taz a écrit :

bah alors tu t'en fous du shell, il sert à rien, exécute directement la commande.


 
 [:plusun] Je ne comprend pas l'interet d'un nouveau shell sauf peut être si tu as des variables d'environement dans tes programmes.

mood
Publicité
Posté le 03-06-2008 à 10:16:23  profilanswer
 

n°1740683
Taz
bisounours-codeur
Posté le 03-06-2008 à 10:22:48  profilanswer
 

bah ça change rien, l'environnement suit toujours, shell ou pas.

n°1740686
h4rold
Posté le 03-06-2008 à 10:30:25  profilanswer
 

Taz a écrit :

bah ça change rien, l'environnement suit toujours, shell ou pas.


 
Je pensait plutot a un conflit entre les applications qui utilisent les memes variables d'environement.

n°1740688
Taz
bisounours-codeur
Posté le 03-06-2008 à 10:35:25  profilanswer
 

bah l'environnement s'hérite, les modifications ne se propagent pas de fils en père.

n°1740724
matafan
Posté le 03-06-2008 à 11:25:14  profilanswer
 

J'ai besoin d'exécuter un shell car mes lignes de commandes sont des expressions shells. Elles peuvent contenir des redirections, des constructions complexes... Que je ne peux pas interpréter moi même. Donc il faut que je les fasse exécuter par un shell.

n°1740729
matafan
Posté le 03-06-2008 à 11:35:22  profilanswer
 

Ah ah magnifique, j'ai une solution assez intéressante :D
 
Je passe ma ligne de commande sur le stdin du shell, comme d'hab. Par contre, les données que je veux envoyer à ma ligne de commande, je les passe au shell sur le file descriptor 42. Ensuite en interne il suffit que j'entoure la ligne de commande avec "{ ligne_de_commande; } <&42" et le tour est joué, ma ligne de commande lit les données qui arrivent sur le fd 42. Plus crade tu meurs :lol:
 
Edit : <&42, pas <42


Message édité par matafan le 03-06-2008 à 11:43:53
n°1740735
gilou
Modérateur
Modzilla
Posté le 03-06-2008 à 11:41:40  profilanswer
 

J'allais justement te proposer une solution de ce type.
Ca ne me semble pas crade du tout, mais la maniere logique de proceder dans ce contexte: puisque les fd standards ne sont pas dispo (a cause de ton echo), en creer d'autres et les utiliser.
A+,


Message édité par gilou le 03-06-2008 à 11:42:17

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
n°1740738
matafan
Posté le 03-06-2008 à 11:44:27  profilanswer
 

Oui mais bon utiliser un numéro de fd hardcodé (42) c'est pas super.

n°1740778
Taz
bisounours-codeur
Posté le 03-06-2008 à 12:40:30  profilanswer
 

Pourquoi les fichiers temporaires c'est pas génial au fait ?

n°1740789
matafan
Posté le 03-06-2008 à 13:11:25  profilanswer
 

Les fichiers temporaires c'est pas super niveau perf je pense. Mon truc doit exécuter des tas de commandes en raffale. La sécurité c'est pas forcément génial non plus, quoi qu'avec mktemp et des droits très restrictifs ça doit passer.
 
Enfin je pense qu'au final je vais prendre la solution du fd 42, sauf que j'étais un peu bête... Pas besoin de hardcoder un fd à utiliser, il suffit de générer la chaine "<&n" avec comme n le file descriptor du coté lecture du pipe.
 
Pour ceux que ça intéresse j'ai codé un petit programme de test qui montre comment ça marche. Le premier argument est la ligne de commande, le deuxième argument est la chaine qu'on veut envoyer à la ligne de commande :
 

Code :
  1. #include <sys/types.h>
  2. #include <sys/wait.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <unistd.h>
  7. int
  8. main(int argc, char *argv[])
  9. {
  10.         int cmdfd[2], datafd[2];
  11.         char *cmd, *data = NULL;
  12.         pid_t pid;
  13.         if (argc < 2 || argc > 3 ) {
  14.                 fputs("Usage: shexec Command [Data]\n", stderr);
  15.                 exit(1);
  16.         }
  17.         cmd = argv[1];
  18.         if (argc >= 3) {
  19.                 data = argv[2];
  20.         }
  21.         /* Create pipes for command and data */
  22.         if (pipe(cmdfd) || pipe(datafd)) {
  23.                 perror("pipe()" );
  24.                 exit(1);
  25.         }
  26.         pid = fork();
  27.         if (pid == -1) {
  28.                 perror("fork()" );
  29.                 exit(1);
  30.         }
  31.         if (pid == 0) {
  32.                 /*** Child ***/
  33.                 /* Close "write" side of pipes */
  34.                 close(cmdfd[1]);
  35.                 close(datafd[1]);
  36.                 /* Commands are read on stdin */
  37.                 if (-1 == dup2(cmdfd[0], 0)) {
  38.                         perror("dup2()" );
  39.                         exit(1);
  40.                 }
  41.                 /* Execute shell */
  42.                 execl("/bin/sh", "sh", (char *)NULL);
  43.                 perror("execl()" );
  44.                 exit(1);
  45.        
  46.         } else {
  47.                 int status;
  48.                 char buf[1024];
  49.                 /*** Parent ***/
  50.                 /* Close "read" side of pipes */
  51.                 close(cmdfd[0]);
  52.                 close(datafd[0]);
  53.                 /* Connect command line's stdin to data pipe */
  54.                 snprintf(buf, sizeof buf,  "{ %s; }<&%d", cmd, datafd[0]);
  55.                 buf[sizeof buf - 1] = '\0';
  56.                 printf("      Parent: %d\n", getpid());
  57.                 printf("       Child: %d\n", pid);
  58.                 printf("     Command: %s\n", buf);
  59.                 printf("        Data: %s\n", data);
  60.                 /* Send command line */
  61.                 write(cmdfd[1], buf, strlen(buf));
  62.                 /* Send data */
  63.                 if (data) {
  64.                         write(datafd[1], data, strlen(data));
  65.                 }
  66.                 close(cmdfd[1]);
  67.                 close(datafd[1]);
  68.                 wait(&status);
  69.                 printf("Child status: 0x%08x\n", status);
  70.                 exit(0);
  71.         }
  72.         return 0;
  73. }


 
A l'exécution ça donne par exemple :

$ ./shexec "cat > out" "How you doin'"
      Parent: 27108
       Child: 27109
     Command: { cat > out; }<&6
        Data: How you doin'
Child status: 0x00000000
$ cat out
How you doin'


Et ça marche parfaitement :sol:


Message édité par matafan le 03-06-2008 à 13:16:40
n°1740802
Taz
bisounours-codeur
Posté le 03-06-2008 à 13:32:13  profilanswer
 

le problème c'est que tu squatte un descripteur.
 
Les fichiers temporaires:
- niveau sécurité, y a pas vraiment de problème avec les bons droits. Ou créer tes fichiers dans un dossier spécifique
- niveau perf: optimisation prématurée. Est-ce que tu as un problèmes de perf ? Avec un FS moderne, ton fichier temporaire, il ne touchera jamais le disque, il restera en cache.
 
En solution intermédiaire, tu as les tubes nommés.
 
Ton code est pas top puisque tu ne vérifies pas le retour des write.

n°1740857
matafan
Posté le 03-06-2008 à 14:31:41  profilanswer
 

Mon code c'est un programme de test pour valider l'idée, il vérifie déjà bien trop de choses ;)

n°1740904
Taz
bisounours-codeur
Posté le 03-06-2008 à 15:48:09  profilanswer
 

snprintf met le 0 final toute seule.

n°1740943
matafan
Posté le 03-06-2008 à 16:59:36  profilanswer
 

Ah oui tient, j'ai confondu avec strncpy.

mood
Publicité
Posté le   profilanswer
 


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  Shell/Batch

  [shell] Passer stdin à une commande passé au shell sur stdin

 

Sujets relatifs
problème avec la commande cutpasser de mac à windows
Encodé un mot de passecommande unix pour tester 2 valeurs
[RESOLU] problème script et commande awkPasser un objet Array à une fonction JS
Emulateur mini-shellProgramme C serie de commande
COMMANDE DOSTrouver mot de passe
Plus de sujets relatifs à : [shell] Passer stdin à une commande passé au shell sur stdin


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR