J'avais besoin d'un truc pour relancer une commande au plus un certain nombre de fois et attendre un certain temps entre deux exécution pour une bricole, alors j'ai pondu ça. Ça aurait pu me prendre 5 lignes de shell, mais c'est le genre de trucs que j'aime bien avoir sous le $PATH, et implémenter ça en vrai shell me semblais un peu du gâchis (à noter, ça existe)
J'ai fait quelques tests rapides, ça semble marcher, je pousserai plus demain pour voir si ça s'intègre bien dans mes scripts, en attendant je vous passe le source, dès fois que j'oublie alors que ça aurait pu servir a quelqu'un…
//Copyright 2020
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
//to build:
//cc retry.c -o retry
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/unistd.h>
#include <errno.h>
#include <limits.h>
#define VERSION "0.1.0"
#define CHECK_ERR() \
do {\
if( errno ){ \
fprintf( stderr, "[%d]: %s\n", __LINE__, strerror( errno ) ); \
return EXIT_FAILURE; \
}\
} while( 0 )
#define INT_ARG( ival ) \
do {\
ival = strtoul( val, &endptr, 0 );\
if( ival > INT_MAX || *endptr )\
{ \
fputs( "invalid " #ival " value\n", stderr );\
return EXIT_FAILURE;\
} \
CHECK_ERR();\
} while( 0 )
int run_cmd( unsigned int time, unsigned int tsleep, char** cmd );
int print_bad_opt( char const* opt );
int print_version( void );
int print_help( int ret );
int main( int argc, char** argv )
{
unsigned long count = 0;
unsigned long pause_time = 0;
unsigned long sleep_time = 0;
char** arg = argv;
char** cmd = argv + argc;
char* endptr = 0;
while( *arg )
{
++arg;
if( !*arg || ( *arg && **arg != '-' ) )
{
return print_bad_opt( *arg );
}
char opt = (*arg)[1];
char* val = &(*arg)[2];
switch( opt )
{
case '-':
cmd = arg + 1;
arg = argv + argc;
break;
case 'v':
return print_version();
case 'h':
return print_help( EXIT_SUCCESS );
case 's':
INT_ARG( sleep_time );
val = endptr;
break;
case 't':
INT_ARG( pause_time );
val = endptr;
break;
case 'c':
INT_ARG( count );
val = endptr;
break;
default:
return print_bad_opt( *arg );
}
}
if( !( *cmd ) )
{
return print_help( EXIT_FAILURE );
}
if( count == 0 )
{
while( run_cmd( (unsigned)pause_time, (unsigned)sleep_time, cmd ) )
{
}
return EXIT_SUCCESS;
}
for( ; count && run_cmd( (unsigned)pause_time, (unsigned)sleep_time, cmd ); --count )
{
}
return count ? EXIT_SUCCESS : EXIT_FAILURE;
}
int run_cmd( unsigned int time, unsigned int tsleep, char** cmd )
{
sleep( tsleep );
int status;
switch( fork() )
{
case 0:
alarm( time );
execvp( *cmd, cmd );
case -1:
CHECK_ERR();
break;
default:
break;
}
pid_t child = wait( &status );
return child != -1 ? WEXITSTATUS( status ) : -1;
}
int print_bad_opt( char const* opt )
{
fprintf( stderr, "Unknown option: %s\n", opt );
return print_help( EXIT_FAILURE );
}
int print_version( void )
{
fputs( VERSION "\n", stderr );
return EXIT_SUCCESS;
}
int print_help( int ret )
{
fputs( "retry [OPTIONS] -- command arguments\n"
"\trun a command until it returns successfully.\n"
"\t\n"
"\tOptions:\n"
"\t-v: print the version number of this program\n"
"\t-h: print this screen\n"
"\t-tTIME: if the command didn't return in TIME seconds, sends it SIGARLM. 0 means no limit.\n"
"\t-cCOUNT: try running the command up to COUNT times. 0 means no limit.\n"
"\t-sTIME: waits TIME seconds before executing command. 0 is invalid.\n"
, ret == EXIT_FAILURE ? stderr : stdout );
return ret;
}
Sur ce, bonne nuit.
PS: si vous préférez mettre la licence en CC-0, ou autre trucs sans prise de tête, faites.
# bash
Posté par harlock974 . Évalué à 7.
Personnellement je fais :
for i in `seq 3`;do commmande;sleep delai;done
Mais c'est vrai que c'est du shell bash.
[^] # Re: bash
Posté par batousky . Évalué à 10.
J'ajouterai un break pour sortir a la première réussite:
Mais c'est vrai que c'est du shell sh ;).
[^] # Re: bash
Posté par freem . Évalué à 2.
La ou c'est chiant, c'est quand tu as plusieurs fois le même bloc de code avec juste la commande qui change.
Du coup tu fais une fonction. Sauf que du coup si tu veux être propre faut vérifier les arguments…
Mais bon, on va pas se mentir: en vrai quand j'avais le nez plongé dans mon shell, c'est plus ou moins ce que j'ai fait:
[^] # Re: bash
Posté par batousky . Évalué à 3.
Si t'es le seul a utiliser la fonction t'as pas forcement besoin de tester les arguments.
En tant que j'y suis, ton code exécute 2 fois
$CMD
quand la commande marche, les 'cd' servent a rien vu que tu utilise $SOCK_PATH, et die… on sais pas ce que ça fait…[^] # Re: bash
Posté par wismerhill . Évalué à 5.
Si c'est du bash, alors tu peux éviter la sous-commande:
# systemd
Posté par Guillaume D. . Évalué à 9. Dernière modification le 22 avril 2020 à 09:51.
bonjour,
tu peux utiliser systemd et les options qui vont bien :
[Service]
Type=forking
PIDFile=/toto/pids/delayed_job.pid
RemainAfterExit=no
Restart=on-failure
RestartSec=50s
StartLimitInterval=400s
StartLimitBurst=3
Mais c'est pas du Shell ni du C…
[^] # Re: systemd
Posté par freem . Évalué à 3.
Oups, mon script se lance en userspace sur un bsd ou un windows… et ce retry, c'est juste une partie d'un tout.
Systemd a son utilité, mais pas ici. Non, parce que hein, sinon j'aurai utilisé runit.
# watch
Posté par Colin Pitrat (site web personnel) . Évalué à 3.
Ça serait bien d'ajouter une option à"watch" pour ça … C'est le premier endroit où j'aurai cherché ça.
[^] # Re: watch
Posté par freem . Évalué à 2.
Honnêtement, je n'y avais pas pensé. D'un autre côté pour moi, watch c'est plutôt un truc interactif, un peu comme less: pas un truc que je penserais a utiliser dans un script.
[^] # Re: watch
Posté par batousky . Évalué à 2.
On peut toujours le faire, mais ça commence a être vraiment tordu:
# fonction
Posté par Psychofox (Mastodon) . Évalué à 3. Dernière modification le 22 avril 2020 à 13:27.
Pourquoi ne pas en faire une fonction bash dans ton profile?
D'autant plus que ça le rendrait facile à synchroniser sur toute machine qui récupère ton profile depus un dépot git ou autre, portable et que si tu l'écris en bourne shell pur il est portable sur à peu près tous les unix-likes, même ceux tournant avec busybox, ash ou korn shell et ce sans aucune compilation.
Là dans le genre j'ai l'impression que tu as sorti un lance-flamme pour alumer une cigarette.
[^] # Re: fonction
Posté par Psychofox (Mastodon) . Évalué à 3. Dernière modification le 22 avril 2020 à 13:28.
Et j'ai oublié de dire, sur n'importe quelle archi de processeur.
[^] # Re: fonction
Posté par freem . Évalué à 2.
Chiant a distribuer ensuite, de mettre ça dans le fichier
.profile
. Mais c'set vrai que je devrais me faire une lib d'utilitaire shell, c'est lourd de réimplémenter die() a chaque fois, par exemple (c'est que 5 lignes, hein, mais bon).Copier un fichier, c'est copier un fichier… le coller dans le $PATH d'une manière ou d'une autre, que ce soit un binaire ou un shell script, c'est pareil.
Certes. Mais bon… ici, c'est littéralement
cc retry.c -o retry
pour compiler. Ça ne vaut même pas le coup de faire un makefile…Honnêtement, c'est quand j'ai vu le code en bash de 160 lignes qui faisait plus ou moins ça que je me suis motivé a m'amuser. Et je venais de me farcir du shell pendant quelques temps, notamment avec
jq
(pas mon expérience la plus agréable) du coup le code un peu "bourrin" c'est vrai me démangeais.Je voulais voir un peu ce que ça donnerait en C, réputé nécessiter beaucoup plus de lignes que la majorité des autres (bon, si je voulais vraiment jouer au NLOC le plus bas, j'utiliserais pas ce style de formatage, certes).
Je pensais que ça prendrais un peu plus de lignes à la base en vrai.
Je n'ai pas implémenté a fonctionnalités égales non plus je sais: le code bash n'implémentais pas la fonctionnalité de timeout (logique, autant se baser sur la commande dédiée), et semble implémenter 2-3 autres trucs dont je n'ai pas trop compris l'intérêt.
Probablement qu'il peut aussi gérer d'autres unités que les secondes.
Hum… je vois pas ou ce bout de code ne serais pas compatible sur toute archi? Selon les manpages, ce code nécessite: POSIX.1-2001 et C89. A vue de nez, c'est l'usage de
sleep
qui empêcherais l'usage de 4.3BSD, mais ça serait trivial de le remplacer par:usleep( 1000*time)
.Franchement, je doute qu'il existe encore tant d'archi pour lesquelles il n'y ait aucun compilo qui ne supporte pas C89 et POSIX.1-2001, et pour lesquelles un bash soit implémenté (1ère version de bash date de 89 selon wikipedia).
[^] # Re: fonction
Posté par batousky . Évalué à 1.
T'as juste a faire
source ma_lib.sh
, ce qui permet d'aller voir le code plus facilement, pour debugué par exemple.Ça fait juste 2 fichiers à distribuer.
Les dossier du $PATH sont protégé par les droits root, c'est pas parreil.
Tu sacrifie la portabilité et le sécu de ta machine, juste pour ne pas écrire 3 lignes une fois.
160 lignes c'est trop long pour du bash.
T'es obligé de compiler pour chaque archi.
Avoir un compilateur sur un système élargit sa surface d'attaque, c'est en partie pour ça qu'il n'est généralement par installé par défaut.
[^] # Re: fonction
Posté par freem . Évalué à 2.
Versus un seul?
Euh… je sais pas pour toi, mais moi, j'ajoute souvent un dossier de mon $HOME à mon $PATH, et j'y colle mes binaires perso… donc non, c'est pas protégé, et c'est pas une question de config perso, parce qu'un programme ça peut modifier l'environnement de ses fils de toute façon.
La sécu? Ou ça?
Je me suis dit la même, pour le coup.
pour ça et lautre point, ok.
[^] # Re: fonction
Posté par batousky . Évalué à -1.
Un seul + le fichier qui modifie ton PATH
Bah c'est une mauvaise pratique, tu peux éventuellement le faire pour ton shell courant, mais de manière global (pour une utilisation dans un script) c'est pas une bonne idée.
Par exemple, si tu cré un fichier "test" exécutable dans ton home, et que tu exécute un script qui utilise la commande test, il va exec ton test a la place et potentiellement faire n’importe-quoi.
Je vais pas continué a débattre, et t'as bien le droit de faire ce que tu veux, mais si j'ai un conseil a te donner, c'est de rester proche des standards, fait des fonctions, et dans 10 ans, ça marchera peut être toujours :)
Et généralement on a pas besoin de commande
retry
, en dehors d'une utilisation pour téster/débuguer. Si t'as une commande qui peux échouer pour de bonne raison, elle doit très certainement avoir une option "retry", "timeout", "wait" ou quelque chose comme ça.[^] # Re: fonction
Posté par freem . Évalué à 2.
Une… mauvaise pratique, de montrer mon code a tous, anonymement?
La, il va falloir que tu m'expliques… parce que tu vois, mon code, ici, il a permis potententiellement pleins de trucs.
Déjà, il fait le job. Rien que ça, c'est pas dégueu, l'air de rien.
En plus, il est amélioré par des commentaires, c'est donc utile pour les futurs hackers.
Ah et… il te reste a démontrer qu'il sert a rien… oups! C'est vrai, il y a 0 pôlitique. C'est de la techique pure, et je remercie ceux qui ont dit que même si c'est mauvais un peu de code ça fait du bien. Ce site fut un repaire de geeks voires nerds, cest devenu un repaire de politiciens, et ça me gonfle.
Perso, j'entend changer ça, par la pratique. Je code ou je meurs.
[^] # Re: fonction
Posté par freem . Évalué à 1.
Je me réponds a moi-même, mais, malheureusement, je suis français. Comment je fais pour mettre ce que je publie ici en CC0? Et si un jour je veux juste une licence type BSD-2, je fais comment, je fais un double compte ou bien je me tais?
[^] # Re: fonction
Posté par barmic 🦦 . Évalué à 2.
Il n'a pas remis en question le fait que tu publie du code. Il dit juste que selon lui il faut que ça arrive dans
/usr/local/bin
sinon c'est pas HFS.https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
[^] # Re: fonction
Posté par barmic 🦦 . Évalué à 3. Dernière modification le 26 avril 2020 à 13:16.
Ton commentaire semble montrer que tu n'a pas compris sa remarque. Il ne parle pas de mettre le répertoire courant dans son
${PATH}
, mais un dossier particulier. C'est une technique très classique et très utilisée (avoir un${HOME}/bin
qui est ajouté dans${PATH}
).Le problème que tu décris n'existe que si le dossier en question est ajouté en première position ce qui est rarement fait.
Tu considère comme général ta propre vision, mais elle n'est ni unique, ni une généralité. Une logique plus unix serait de laisser les nouvelles tentatives à la charge d'un autre outil. D'où l'existence de la commande
timeout
dans lescoreutils
.https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
# Petite review
Posté par barmic 🦦 . Évalué à 4.
Je ne suis pas un grand connaisseur du C et ça fait longtemps que je n'ai pas pratiqué.
Mais lire du code c'est toujours amusant :)
Il y a pleins de subtilité dans ce petit bout code. Faut voir que
execvp()
ne retourne jamais sauf en cas d'erreur où il positionneerrno
. Donc la macroCHECK_ERR()
, va gérer les cas d'erreur defork()
(ce qui est évident) ou deexecvp
()` (ce qui est déjà plus subtile).La gestion du fils est hors du
switch
alors que sémantiquement elle devrait être dans ledefault
(amha). Et on a du coup un truc qui est de genre :Bien sûr ce n'est qu'un avis personnel qui est là pour ouvrir une discussion :)
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
[^] # Re: Petite review
Posté par Renault (site web personnel) . Évalué à 6.
Le code reste quand même assez discutable. Même si cela reste un petit bout de code il y a de quoi améliorer
Déjà les macros CHECK_ERR et INT_ARG n'ont aucun intérêt, faire des fonctions à la place serait plus lisible et permettrait au compilateur de produire des messages d'erreurs plus sympas.
D'autant que le second est erroné. Il fait appel à des variables qui ne son présentes que dans main et qui ne sont pas passés en paramètres. Donc en somme cette macro n'est pas réutilisable alors que cela ne coûterait rien de le faire en fonction.
Il est préférable d'initialiser un pointeur à NULL plutôt que 0.
Il vérifie que l'argument débute par un tiret mais ne vérifie pas la taille pour être sûr que l'argument a bien plus d'un caractère ce qui serait je pense pertinent.
Puis il vérifie deux fois que arg n'est pas nul, c'est inutilement redondant.
Ce n'est pas une erreur en soi mais la ligne me semble inutilement longue et complexifie la lecture de la boucle. La commande devrait être dans le corps de la boucle quitte à utiliser un do while qui serait plus judicieux ici.
Globalement le main je le découperais en plein de fonctions encore, il y a moyen.
Bref, c'était ma revue de code. Elle vaut ce qu'elle vaut.
[^] # Re: Petite review
Posté par freem . Évalué à 2.
Techniquement, c'est du C, et en C, NULL, c'est 0. Après, oui, c'est plus lisible NULL.
En même temps, je vérifie le 2nd caractère dans le switch. Du coup, déjà je check si '\0' ou '-', puis je vérifie si le 2nd est connu. Si le 2nd est pas connu, ce qui implique le '\0', je pars en erreur.
Pas faux.
Et je vous remercie tous les deux, c'est toujours sympa une revue.
C'est clair que le code est améliorable, j'ai fini de bricoler ça a tôt hier matin faut dire ^ (bien que ça ne soit qu'une mauvaise excuse… bon, on va faire comme si jétais prof: c'étais pour l'exercice… non?)
[^] # Re: Petite review
Posté par freem . Évalué à 1.
C'est vraiment si moche? Je serais heureux que tu me montre par l'exemple, ça servira potentiellement a plein de gens.
C'est vrai, qui suis-je au fond, pauvre libriste qui ose publier du code si sale que personne ne publie la version correcte?
C'est triste, mais ici, soit les gens mettent des liens githubs, dont le côté libre est discutable, soit on se fait limite démolir pour avoir osé ne pas être parfait..
Pardon mais moi, j'aime pas le libre parce que c'est du code parfait, mais parce que c'est du code que les autres peuvent améliorer. Sans se baser sur une base propriétaire comme github ou gitlab.
Je veux être libre de filer mon code a qui je veux, c'est a dire: tout le monde, même si c'est mauvais, certains apprendront, et moi je lirais leurs articlcles… techniques, et non politiques, ça me changera!
[^] # Re: Petite review
Posté par barmic 🦦 . Évalué à 3.
Euh… Je comprends qu'ils puisse avoir un ton qui ne te convient pas, mais c'est toute de même une critique constructive. Je ne sais pas trop pourquoi tu le prends à parti comme ça. Il a pris le temps de revoir ton code et d'en exprimer une critique. C'est un comportement précieux dans le libre. Parce que le libre ce n'est pas que du code, c'est aussi de la remonté de bug, de la relecture, du tris de bug, de l'écriture de documentation, du packaging,…
Il n'a pas remis en cause ton droit d'avoir écris ton code et de l'avoir partagé. Le fait qu'il ai pris le temps de relire et de rédiger remarques montre justement une certaine considération.
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
[^] # Re: Petite review
Posté par Renault (site web personnel) . Évalué à 4.
Comme barmic l'a dit tu écris et publies ce que tu veux, c'est ton droit.
Non ton code n'est clairement pas parfait, il ne faut pas le prendre mal, j'ai donné les éléments qui m'ont sauté aux yeux pour que tu puisses faire mieux la prochaine fois. Moi aussi j'ai écrit (et j'écris à l'occasion) du code sale. Et je suis content quand quelqu'un m'explique ce que je pourrais améliorer.
Bref, ne prends pas la mouche, il n'y a pas de raisons…
[^] # Re: Petite review
Posté par freem . Évalué à 2.
Non, c'est la gestion du père. Le fils, c'est dans le cas du 0. Ça doit être une typo compte tenu de la suite cela dit, mais bref, oui, en soit tu as raison.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.