Jouer avec un programme

a marqué ce sujet comme résolu.
Auteur du sujet

Bonjour,

J’ai récemment découvert la commande mkfifo. J’ai décidé de jouer un peu avec. Mais il y a un truc que je n’arrive pas a faire: Contrôler simultanément le couple stdin stdout d’un simple programme.

Programme en C:

//gcc test.c
#include <stdio.h>
int main()
{
    char chaine [6] = {};
    while (1)
    {
        scanf(" %[^\n]s", chaine);
        chaine[2]='.';
        printf("%s\n", chaine);
    }
    return 0;
}

En Bash,

Ceci marche:

mkfifo /tmp/input /tmp/output

./a.out < /tmp/input &
exec 3> /tmp/input
echo AAAAA >&3
echo BBBBB >&3
exec 3>&-

killall ./a.out
rm /tmp/input /tmp/output

Ceci marche:

mkfifo /tmp/input /tmp/output

cat < /tmp/input | cat > /tmp/output &
exec 3> /tmp/input
echo AAAAA >&3
head -1 < /tmp/output
exec 3>&-

killall ./a.out
rm /tmp/input /tmp/output

Ceci marche:

mkfifo /tmp/input /tmp/output

cat < /tmp/input | ./a.out &
exec 3> /tmp/input
echo AAAAA >&3
exec 3>&-

killall ./a.out
rm /tmp/input /tmp/output

Mais ceci ne marche pas:

mkfifo /tmp/input /tmp/output

cat < /tmp/input | ./a.out | cat > /tmp/output &
exec 3> /tmp/input
echo AAAAA >&3
head -1 < /tmp/output
exec 3>&-

killall ./a.out
rm /tmp/input /tmp/output

Je ne comprends pas pourquoi le dernier ne marche pas? Le but final étant de contrôler les entrées-sorties d’un programme via deux fifo dans un premier temps, puis avec une seule si possible.

Merci d’avance

Édité par Taurre

+0 -0

Salut,

Sous cette apparence simple, il y a pas mal de choses à dire. ^^

Tout d’abord, ton code C a plusieurs lacunes :

  1. Tu ne limites pas la quantité de données lues en entrée par rapport à la taille de ton tableau chaine.
  2. Ta chaîne de format <espace>%[^\n]s signifie : « lit des données et ignore-les tant qu’il s’agit d’espaces, puis stocke dans la chaîne chaine une suite de caractères ne comportant pas de \n suivie par un 's' ».
  3. Tu modifies le troisième caractère de la chaîne chaine sans t’assurer du nombre de caractères lus (ce serait dommage d’écraser le caractère nul).
  4. Tu ne vérifies pas le retour de tes fonctions pour d’éventuelles erreurs.
  5. Tu utilises une boucle infinie sans condition de sortie (mais bon, c’est un code de test, donc osef).

Un meilleur code pour faire tes tests serait celui-ci :

#include <stdio.h>
#include <stdlib.h>


static void
chomp(char *s)
{
    while (*s != '\n' && *s != '\0')
        ++s;

    *s = '\0';
}


int
main(void)
{
    char buf[255];

    while (fgets(buf, sizeof buf, stdin) != NULL) {
        chomp(buf);
        printf("%s\n", buf);
    }

    if (ferror(stdin)) {
        perror("fgets");
        return EXIT_FAILURE;
    }

    return 0;
}

Ensuite, pour ton problème principal, ce dernier vient de la temporisation des données réalisée par la bibliothèque C. Par défaut, sauf si un flux est lié à un terminal, les données sont stockées dans un tampon avant d’être lues ou écrites. Dans le cas de la lecture, cela ne pose pas de soucis pour ce cas de figure, en revanche, côté écriture, si.

Dans le cas où tu fais appel à ton programme C, ce dernier stocke la chaîne AAAAA\n dans le tampon du flux stdout et n’écrit donc rien dans le FIFO output (il ne le fera qu’une fois le tampon plein). Pour éviter cela tu dois soit faire appel à la fonction fflush() après ton appel à printf(), soit modifier le type de tampon du flux stdout (avec setbuf() ou setvbuf()). Le concept est en partie abordé dans cette section du tuto C.

En bref, le programme suivant devrait te donner le comportement que tu attends.

#include <stdio.h>
#include <stdlib.h>


static void
chomp(char *s)
{
    while (*s != '\n' && *s != '\0')
        ++s;

    *s = '\0';
}


int
main(void)
{
    char buf[255];

    while (fgets(buf, sizeof buf, stdin) != NULL) {
        chomp(buf);
        printf("%s\n", buf);

        if (fflush(stdout) != 0) {
            perror("fflush");
            return EXIT_FAILURE;
        }
    }

    if (ferror(stdin)) {
        perror("fgets");
        return EXIT_FAILURE;
    }

    return 0;
}

Tiens, pour les curieux, petite énigme du soir : pourquoi n’y a-t-il pas de processus créé ?

$ mkfifo input
$ cat <input &
$ ps -U taurre | grep cat
# Rien...
$ 

Édité par Taurre

+0 -0

Tiens, pour les curieux, petite énigme du soir : pourquoi n’y a-t-il pas de processus créé ?

$ mkfifo input
$ cat <input &
$ ps -U taurre | grep cat
# Rien...
$ 

Taurre

Humm, bash fait un fork (clone) puis exec... ce qui ne change pas le nom du nouveau process ?

C’est curieux execve devrait écraser le nom du process 🤔

ache.one                 🦹         👾                                🦊

+0 -0

Humm, bash fait un fork (clone) puis exec... ce qui ne change pas le nom du nouveau process ?

C’est curieux execve devrait écraser le nom du process 🤔

ache

En fait, cela tient à une propriété des FIFO : s’il est ouvert en lecture, l’ouverture est bloquante tant qu’aucun processus ne l’a ouvert en écriture. Du coup, tant qu’aucun processus n’ouvrira le fichier input en écriture, le fork de bash sera bloqué et ne pourra pas exécuter cat. ;)

Édité par Taurre

+1 -0
Auteur du sujet

Merci pour ces informations intéressantes comme toujours avec ta plume.

J’ai mal dû définir ce que je cherchais à savoir dans ce poste.

Ce n’est pas particulièrement une réponse orientée C que je cherchais, mais une orientée Bash. Une méthode "universelle" qui fonctionnerais même avec un exécutable avec un code peu raffiné comme celui que j’ai présente ci-dessus.

+0 -0

Ce n’est pas particulièrement une réponse orientée C que je cherchais, mais une orientée Bash. Une méthode "universelle" qui fonctionnerais même avec un exécutable avec un code peu raffiné comme celui que j’ai présente ci-dessus.

Abracadabra

Dans le cas que tu as présenté, le souci était du côté du code C. Par ailleurs, à supposer que tu utilises un programme similaire écrit dans un autre langage, il est probable que tu aies le même soucis, pas mal d’autres langages faisant en fait appel aux fonctions de la bibliothèque C.

Maintenant, si tu as un autre exemple pour décrire ce que tu veux dire par « Une méthode "universelle" qui fonctionnerais même avec un exécutable avec un code peu raffiné comme celui que j’ai présente ci-dessus » je peux essayer de te donner une autre réponse.

Édité par Taurre

+0 -0
Auteur du sujet

Ce que je veux dire, c’est confronté à un programme boite noire recevant/attendant des inputs via stdin et émettant des outputs via stdout en réponse, le tout en boucle.

Comment avec un ou plusieurs fifo Bash le cloisonné. Pour par exemple, l’inclure dans un script automatisant son utilisation. Avec ultérieurement à l’appel du programme, car par exemple en background, lui passé des entrées via un echo blabla > ou autre et lire ces sorties via un head -1 < ou autre.

Dans le style de l’exemple suivant:

mkfifo /tmp/input /tmp/output

cat < /tmp/input | ./BoiteNoire | cat > /tmp/output &

echo AAAAA > /tmp/input
head -1 < /tmp/output
echo BBBBB > /tmp/input
head -1 < /tmp/output
…

Mais l’idéal serait un truc minimaliste ressemblant à

mkfifo /tmp/input /tmp/output

./BoiteNoire < /tmp/input > /tmp/output &

echo AAAAA > /tmp/input
head -1 < /tmp/output
echo BBBBB > /tmp/input
head -1 < /tmp/output
…

Voir même à

exec 3<> ./BoiteNoire
echo AAAAA >&3
head -1 <&3
echo BBBBB >&3
head -1 <&3
…

Dans le cas d’un binaire lancé avec bash, j’arrive facilement à trouver comment connecter un input avec un fifo OU un output avec un fifo, mais je n’arrive pas trouver comment connecter en même temps un input avec un fifo ET un output avec un (autre) fifo sur le même programme.

Dans le cas d’un socket, en faisant par exemple exec 3<> /dev/tcp/IP/PORT cela marche très bien. Je souhaite donc trouver un moyen i.e. la syntaxe bash correcte pour faire quelque chose d’équivalant avec un binaire.

Édité par Abracadabra

+0 -0

Mmm… Le tout, c’est que tu utilises foncièrement déjà la bonne méthode, ou en tous les cas celle que tu peux utiliser avec bash. À ma connaissance, tu butes ici sur des éléments qui ne sont pas gérables au niveau de bash, mais qui sont inhérents aux FIFOs ou liés à la bibliothèque C. Notamment, l’ouverture d’un FIFO est bloquante tant qu’un autre processus ne l’a pas ouvert en écriture ou en lecture. De même, la temporisation n’est pas gérable en bash sauf via des commandes spécifiques comme stdbuf, mais elle ne fonctionne que si ton programme utilise la bibliothèque C du projet GNU (c’est celle utilisée par à peu près toutes les distributions Linux).

Avec stdbuf, cela donnerait ceci :

$ mkfifo input output
$ stdbuf -oL -iL ./a.out <input >output &
$ exec 3>input
$ echo Test >&3
$ head -n 1 output

Édité par Taurre

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte