Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

popen et stderr

13 réponses
Avatar
meta
Bonjour,

J'utilise popen pour exécuter une commande ksh dont je redirige le stderr:
2> /dev/null.

Si le résultat de la commande est bon, aucun message n'est affiché sur
stdout car il est récupéré via le descripteur dans une chaîne de caractères
C et c'est ce que je veux.

Si le résultat n'est pas bon, le message d'erreur de la commande s'affiche
systématiquement malgré la redirection.

De plus, ni le message provenant du perror ni celui du printf (exécutés en
cas de résultat ko) ne sont affichés que ce soit sur le stderr ou le stdout
(pourtant j'ai bien fermé le descripteur du popen avant). Je voudrais
l'inverse ! C.à.d que les messages C soient affichés, mais pas ceux du ksh.

Le premier problème se résout en fermant le stderr mais je voudrais éviter
cela. Peut-on malgré tout imaginer une fermeture temporaire de stderr dont
aura préalablement dup2-liqué le descripteur pour le réouvrir après, mais je
n'ai pas eu le temps de faire la manip ? Existe-t'il une autre solution (je
préfèrerais) ?

Pour le second, je n'ai pas trouvé.

Merci pour toute aide.

10 réponses

1 2
Avatar
espie
In article <4ff74988$0$6816$, meta wrote:
Bonjour,

J'utilise popen pour exécuter une commande ksh dont je redirige le stderr:
2> /dev/null.

Si le résultat de la commande est bon, aucun message n'est affiché sur
stdout car il est récupéré via le descripteur dans une chaîne de caractères
C et c'est ce que je veux.

Si le résultat n'est pas bon, le message d'erreur de la commande s'affiche
systématiquement malgré la redirection.

De plus, ni le message provenant du perror ni celui du printf (exécutés en
cas de résultat ko) ne sont affichés que ce soit sur le stderr ou le stdout
(pourtant j'ai bien fermé le descripteur du popen avant). Je voudrais
l'inverse ! C.à.d que les messages C soient affichés, mais pas ceux du ksh.

Le premier problème se résout en fermant le stderr mais je voudrais éviter
cela. Peut-on malgré tout imaginer une fermeture temporaire de stderr dont
aura préalablement dup2-liqué le descripteur pour le réouvrir après, mais je
n'ai pas eu le temps de faire la manip ? Existe-t'il une autre solution (je
préfèrerais) ?

Pour le second, je n'ai pas trouvé.

Merci pour toute aide.




A ce niveau de detail, en general, vaut mieux descendre d'un niveau et
tout faire directement a coups d'appel posix.

Sauf si ca doit imperativement tourner sur un systeme sans fork, vaut
mieux aller bosser directement avec pipe/fork/fdopen/exec/wait...
Avatar
meta
"Marc Espie" a écrit dans le message de groupe de discussion :
jt7ibc$14ra$

In article <4ff74988$0$6816$, meta wrote:
Bonjour,

J'utilise popen pour exécuter une commande ksh dont je redirige le stderr:
2> /dev/null.

Si le résultat de la commande est bon, aucun message n'est affiché sur
stdout car il est récupéré via le descripteur dans une chaîne de caractères
C et c'est ce que je veux.

Si le résultat n'est pas bon, le message d'erreur de la commande s'affiche
systématiquement malgré la redirection.

De plus, ni le message provenant du perror ni celui du printf (exécutés en
cas de résultat ko) ne sont affichés que ce soit sur le stderr ou le stdout
(pourtant j'ai bien fermé le descripteur du popen avant). Je voudrais
l'inverse ! C.à.d que les messages C soient affichés, mais pas ceux du ksh.

Le premier problème se résout en fermant le stderr mais je voudrais éviter
cela. Peut-on malgré tout imaginer une fermeture temporaire de stderr dont
aura préalablement dup2-liqué le descripteur pour le réouvrir après, mais
je
n'ai pas eu le temps de faire la manip ? Existe-t'il une autre solution (je
préfèrerais) ?

Pour le second, je n'ai pas trouvé.

Merci pour toute aide.


A ce niveau de detail, en general, vaut mieux descendre d'un niveau et
tout faire directement a coups d'appel posix.



Bah oui, mais malgré que je l'aie fait pour le boulot je pose la question
pour l'amour de l'art car le cas qui me pose problème ne se produira pas.

Sauf si ca doit imperativement tourner sur un systeme sans fork, vaut
mieux aller bosser directement avec pipe/fork/fdopen/exec/wait...



Exemple ?... Toujours pour l'amour de l'art :-P
Avatar
espie
In article <4ff74f15$0$2031$, meta wrote:

A ce niveau de detail, en general, vaut mieux descendre d'un niveau et
tout faire directement a coups d'appel posix.



Bah oui, mais malgré que je l'aie fait pour le boulot je pose la question
pour l'amour de l'art car le cas qui me pose problème ne se produira pas.

Sauf si ca doit imperativement tourner sur un systeme sans fork, vaut
mieux aller bosser directement avec pipe/fork/fdopen/exec/wait...



Exemple ?... Toujours pour l'amour de l'art :-P



Tu ne cites pas d'exemple non plus... ton probleme est relativement
flou et peu explique, alors bon, excuse-moi mais parfois c'est un peu
donnant/donnant, hein...

Genre, ce que tu racontais doit certainement fonctionner en l'adaptant,
mais sans code, c'est un peu dur, et puis le manque de precision des
explications conduit facilement a supposer qu'il y a certaines notions
qui ne sont pas totalement acquises ni maitrisees...
Avatar
meta
"Marc Espie" a écrit dans le message de groupe de discussion :
jt7m2s$1626$

In article <4ff74f15$0$2031$, meta wrote:

A ce niveau de detail, en general, vaut mieux descendre d'un niveau et
tout faire directement a coups d'appel posix.



Bah oui, mais malgré que je l'aie fait pour le boulot je pose la question
pour l'amour de l'art car le cas qui me pose problème ne se produira pas.

Sauf si ca doit imperativement tourner sur un systeme sans fork, vaut
mieux aller bosser directement avec pipe/fork/fdopen/exec/wait...



Exemple ?... Toujours pour l'amour de l'art :-P

Tu ne cites pas d'exemple non plus... ton probleme est relativement
flou et peu explique



Je n'ai pas donné d'exemple parce que je croyais m'être exprimée clairement,
mais puisque tu y tiens:

if ((pp = popen("ls toto* | head -1 2> /dev/null","r") != NULL)
{
while (fgets(buf, sizeof(buf), pp)) strcpy(resultat,buf); /* La récupération
se passe ok */
pclose(pp);
}
else
{
/* Affichage NON DESIRE sur stderr du résultat de la commande shell : "toto*
not found" */
perror ("Pas de résultat"); /* Ce message ne s'affiche jamais en cas d'échec
de la commande */
fprintf(stdout,"Pas de résultat"); /* Celui-là non plus */
}

La redirection du stderr via la commande shell n'empêche pas l'affichage non
souhaité.
Si je rajoute: fclose(stderr) avant le popen, l'affichage du résultat de la
commande shell ne se fait pas, mais je voudrais éviter d'avoir à faire cela
car le stderr est utilisé ailleurs. Ma première préoccupation est donc de
savoir comment court-circuiter la redirection du message d'erreur du shell
vers stderr.

Ma question subsidiaire était de savoir si stderr pouvait être fermé le
temps d'exécuter le popen puis réouvert de suite après, mais ce n'est pas ce
qui m'importe le plus car je préfèrerais une autre solution si elle existe.

A contrario, les messages d'erreurs de perror et printf ne sont jamais
affichés alors que je voudrais les voir. C'est moins important mais cet
exemple est tout bête, je l'ai tiré du net et je ne comprends pas ce qui
peut entraver l'affichage de messages sur stdout et stderr.
Avatar
Samuel DEVULDER
Le 07/07/2012 19:27, meta a écrit :

if ((pp = popen("ls toto* | head -1 2> /dev/null","r") != NULL)


...
/* Affichage NON DESIRE sur stderr du résultat de la commande shell :
"toto* not found" */



Heu.. ben comme ca, je ne suis pas super en forme je dirais que c'est
logique. Tu as redirigé le stderr de la partie "head". Celle du ls
est toujours assignée au terminal, et le "ls" est libre de te dire
qu'il n'a rien trouvé pour "toto" et ca arrive à l'écran.

Le "head -1" te sert à récupérer la 1ere ligne je pense. Pourquoi tu
ne gère pas cela dans la boucle lisant le flux issu du "ls toto*" ?
Ca t'éviterait de passer par un pipe.

Mais le mieux je crois est de ne pas faire confiance au shell (plein
de failles de sécurité là dedans), mais comme le dit Marc Espie, de
passer par une couche de plus bas niveau, d'autant plus que celle
là est plutot facile. Tout ce qu'il faut est défini dans <dirent.h>:

==> man opendir
==> man readdir
==> man closedir

A la louche et sans filet ce que tu voudrais faire donnerait:

-------8<----------------------8<----------------------8<---------------
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>

// auxiliaire pour savoir si la chaine s1 débute avec le contenu de s2
#define start_with(s1, s2) ((s1) == strstr((s1), (s2)))

...
DIR *dir;
struct dirent* entry;
char resultat[NAME_MAX]; // resultat
...

dir = opendir("."); // on récupère un objet "dir"
if(NULL == dir) {perror("opendir"); /* ... */ }

*resultat = ''; // init resultat
while(NULL != (entry == readdir(dir))) {
// on vient de lire une entrée. On
// verifie si le nom débute par toto
if(start_with(entry->d_name, "toto")) {
// Si oui, c'est super on a trouvé un "toto*"
strcpy(resultat, entry->d_name);
// le 1er suffit. sinon on laisse la boucle
// se dérouler pour trouver le dernier.
break;
// Question: à quoi correspond être le dernier
// ou le 1er dans un système de fichiers?
}
}
if(-1 == closedir(dir)) {perror("closedir"); /* ... */ }
if(*resultat) { /* on a un toto */ }
-------8<----------------------8<----------------------8<---------------

(c'est très certainement bourré de bug et améliorable, mais l'idée est là).

sam.
Avatar
espie
In article <4ff8718f$0$6129$, meta wrote:

if ((pp = popen("ls toto* | head -1 2> /dev/null","r") != NULL)
{
while (fgets(buf, sizeof(buf), pp)) strcpy(resultat,buf); /* La récupération
se passe ok */
pclose(pp);
}
else
{
/* Affichage NON DESIRE sur stderr du résultat de la commande shell : "toto*
not found" */
perror ("Pas de résultat"); /* Ce message ne s'affiche jamais en cas d'échec
de la commande */
fprintf(stdout,"Pas de résultat"); /* Celui-là non plus */
}




Comme l'a dit Sam, tu rediriges la sortie erreur de head.

Soit tu le fais juste pour ls
ls toto* 2>/dev/null | head -1 2

ou pour tout le pipe;
{ ls toto*|head -1; } 2>/dev/null

Ma question subsidiaire était de savoir si stderr pouvait être fermé le
temps d'exécuter le popen puis réouvert de suite après, mais ce n'est pas ce
qui m'importe le plus car je préfèrerais une autre solution si elle existe.



La, il faut descendre d'un cran.

SANS GESTION D'ERREUR:

int fd[2];

pipe(fd);
int pid = fork();
if (pid == 0) { /* fils */
close(2);
if (fd[1] != 1) {
dup2(fd[1], 1);
close(fd[1]);
close(fd[0]);
execl("/bin/sh", "sh", "-c", "ls toto*|head -1", NULL);
} else { /* papa */
close(fd[1]);
FILE *f = fdopen(fd[0], "r");
fgets sur f....
fclose(f);
int status;
waitpid(pid, &status, 0);
}


Bon, apres, en vrai, bien sur, on va faire ca sur du vrai code qui a besoin
de shell, parce que la, ca se fait tout en C, bien sur...

(glob(3) par exemple).
Avatar
Samuel DEVULDER
Le 07/07/2012 22:41, Marc Espie a écrit :

(glob(3) par exemple).



Je ne connaissais pas. Merci. C'est intéressant. POSIX.2.. on
entend rarement parler de cette partie là (shell & utilities).
La Wikipedia française semble même ne pas la connaitre. :-/

sam.
Avatar
meta
ou pour tout le pipe;
{ ls toto*|head -1; } 2>/dev/null




Mais oui bien sûr ! J'avais tenté d'encadrer l'ensemble avec des parenthèses
mais pas des accolades. J'ai du confondre avec le csh qui était mon premier
shell...

Merci pour ce rappel et pour le reste aussi !
Avatar
meta
Heu.. ben comme ca, je ne suis pas super en forme je dirais que c'est
logique. Tu as redirigé le stderr de la partie "head". Celle du ls
est toujours assignée au terminal, et le "ls" est libre de te dire
qu'il n'a rien trouvé pour "toto" et ca arrive à l'écran.



Oui en effet. J'avais bien tenté d'encadrer l'ensemble de la commande mais
je n'ai pas utilisé ce qu'il fallait.

Le "head -1" te sert à récupérer la 1ere ligne je pense. Pourquoi tu
ne gère pas cela dans la boucle lisant le flux issu du "ls toto*" ?



Parce que je n'ai pas donné la commande complète: ls -t toto*. En fait je
voulais le toto le plus récent et je n'avais pas envie de m'embêter à le
gérer en C, pas le temps non plus :) Sinon il y avait effectivement la
solution de tout faire en C que tu décris mais là encore j'étais trop juste
en temps (Je sais c'est pas dur à faire mais quand on vous met la pression
avec un temps ridiculement court pour tout faire...).
Avatar
Bruno Ducrot
On 07-07-2012, Marc Espie wrote:

SANS GESTION D'ERREUR:



Je ne sais pas si c'est une bonne idée de présenter un code
sans gestion d'erreur, mais bon. Pourquoi pas ?


int fd[2];

pipe(fd);
int pid = fork();
if (pid == 0) { /* fils */
close(2);
if (fd[1] != 1) {
dup2(fd[1], 1);
close(fd[1]);
close(fd[0]);
execl("/bin/sh", "sh", "-c", "ls toto*|head -1", NULL);



Je rajoute systématiquement un exit() ici, juste au cas où exec?()
échoue.


} else { /* papa */
close(fd[1]);
FILE *f = fdopen(fd[0], "r");
fgets sur f....
fclose(f);
int status;
waitpid(pid, &status, 0);
}


Bon, apres, en vrai, bien sur, on va faire ca sur du vrai code qui a besoin
de shell, parce que la, ca se fait tout en C, bien sur...

(glob(3) par exemple).




--
Bruno Ducrot

A quoi ca sert que Ducrot hisse des carcasses ?
1 2