Commande Unix qui renvoie immédiatement un code retour particulier?

28

Existe-t-il une commande Unix standard qui fait quelque chose de similaire à mon exemple ci-dessous

$ <cmd here> 56
$ echo Return code was $?
Return code was 56
$

<cmd here>devrait être quelque chose qui peut être exécuté par fork et laisse 56 comme code de sortie lorsque le processus se termine. Les commandes internes exitet returnshell ne conviennent pas à ce que je recherche, car elles affectent le shell appelant lui-même en sortant. <some cmd>devrait être quelque chose que je peux exécuter dans des contextes non shell - par exemple, invoquer à partir d'un script Python avec subprocess.

Par exemple, /usr/bin/falsesort toujours immédiatement avec le code retour 1, mais j'aimerais contrôler exactement ce qu'est ce code retour. Je pourrais obtenir les mêmes résultats en écrivant mon propre script wrapper

$ cat my-wrapper-script.sh # i.e., <some cmd> = ./my-wrapper-script.sh
#!/usr/bin/bash
exit $1
$ ./my-wrapper-script.sh 56
$ echo $?
56

mais j'espère qu'il se trouve qu'il existe une commande Unix standard qui peut le faire pour moi.

Chris
la source
10
exitest le seul auquel je peux penser, mais il a tendance à mettre fin à votre coquille. bash -c 'exit 56'ou bash -c "exit $1"pourrait travailler pour vous.
Tim Kennedy
12
Et alors (exit 56)?
cuonglm
6
Cela ressemble vraiment à un XYProblem : quel est le but ultime de cela? Pourquoi avez-vous besoin d'une commande qui renvoie comme code de sortie le numéro que vous lui donnez?
Olivier Dulac
1
Eh bien, vous avez le trueet falseintégré si vous devez retourner 0 ou 1.
gardenhead
1
en relation: (non portable) le plus petit programme pour renvoyer une valeur fixe au moment de la compilation: muppetlabs.com/~breadbox/software/tiny/teensy.html
Florian Castellane

Réponses:

39
  1. Une returnfonction basée fonctionnerait, et évite la nécessité d'ouvrir et de fermer un autre shell, (selon le commentaire de Tim Kennedy ):

    freturn() { return "$1" ; } 
    freturn 56 ; echo $?

    Sortie:

    56
  2. en utilisant exitun sous-shell:

    (exit 56)

    Avec des coques autres que ksh93, cela implique de bifurquer un processus supplémentaire est donc moins efficace que ce qui précède.

  3. bash/ zsh/ ksh93seule astuce:

    . <( echo return 56 )

    (cela implique également un processus supplémentaire (et IPC avec un tuyau)).

  4. zshfonctions lambda de:

    (){return 56}
agc
la source
Bonne pensée. Malheureusement, ma question d'origine n'était pas assez bien définie - avoir besoin d '«ouvrir et fermer un autre shell» était presque une exigence de ce que je voulais faire. J'ai édité ma question pour exiger que ce <some cmd>soit une chose exécutable ().
Chris
2
@Chris: Pour n'importe quelle commande shell, vous pouvez la rendre exécutable () en la convertissant en/bin/sh -c ...originalcommand...
psmears
3
Je suis légèrement déçu que? = 56 ne fonctionne pas.
Joshua
@Joshua: peut-être que oui? mais cette affectation a réussi, et vous avez $? mis à 0 ...;)
Olivier Dulac
@OlivierDulac: Ce n'est pas le cas. Il crache une erreur d'analyse.
Joshua
17

Il n'y a pas de commande UNIX standard pour simplement renvoyer une valeur spécifique. Les utilisations de noyau de GNU fournissent trueet falseseulement.

Cependant, vous pouvez facilement l'implémenter vous-même comme ret:

#include <stdlib.h>
int main(int argc, char *argv[]) {
  return argc > 1 ? atoi(argv[1]) : 0;
}

Compiler:

cc ret.c -o ret

Et courir:

./ret 56 ; echo $?

Tirages:

56

Si vous avez besoin que cela fonctionne partout (où bash est disponible, c'est-à-dire) sans installer d'outils supplémentaires, vous devrez probablement recourir à la commande suivante comme l'a suggéré @TimKennedy dans les commentaires:

bash -c 'exit 56'

Notez que la plage valide de valeurs de retour est 0..255 inclus .

tmh
la source
1
trueet falsesont également sous Unix (et même POSIX), pas seulement sous GNU.
Stéphane Chazelas
@ StéphaneChazelas Merci de l'avoir signalé. Comme l'OP l'a mentionné bash, j'ai en quelque sorte supposé GNU - même si la question elle-même dit «Unix».
tmh
14

Si vous avez besoin que l'état de sortie soit défini par une commande exécutée. Il n'y a pas de commande dédiée pour ce 1 , mais vous pouvez utiliser l'interpréteur de n'importe quelle langue qui a la capacité de quitter avec un statut de sortie arbitraire. shest le plus évident:

sh -c 'exit 56'

Avec la plupart des shimplémentations, cela se limite aux codes de sortie 0 à 255 ( shacceptera des valeurs plus élevées mais peut les tronquer ou même provoquer l'envoi d'un signal au processus s'exécutant shcomme avec ksh93 pour les codes 257 à 320).

Un code de sortie peut être n'importe quelle intvaleur integer ( ) mais notez que vous devez le récupérer avec l' waitid()interface pour que la valeur ne soit pas tronquée à 8 bits (sous Linux, elle est toujours tronquée avec waitid()). C'est pourquoi il est rare (et pas une bonne idée) d'utiliser des codes de sortie supérieurs à 255 (utilisez 0-123 pour un fonctionnement normal).

Alternativement:

awk 'BEGIN{exit 56}'
perl -e 'exit 56'
python -c 'exit(56)'
expect -c 'exit 56'

(ceux-ci ne tronquent pas le code de sortie à 8 bits).

Avec NetBSD find, vous pouvez faire:

find / -exit 56

1exit est la commande standard pour le faire, mais étant une fonction intégrée spéciale du shell , il n'y a pas d'exigence qu'il y ait également une commande de ce nom dans le système de fichiers comme pour les fonctions intégrées normales, et la plupart des systèmes n'en incluent pas

Stéphane Chazelas
la source
3
/* Public domain, http://creativecommons.org/publicdomain/zero/1.0/ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char **argv) {
    if(!strcasecmp(argv[0],"true")) return 0;
    else if (!strcasecmp(argv[0],"false")) return 1;
    else if(argc<2) {fputs("One argument required\n",stderr);return 1;}
    else if(!strcasecmp(argv[argc-1],"true")) return 0;
    else if(!strcasecmp(argv[argc-1],"false")) return 1;
    else return atoi(argv[argc-1]);
}

Enregistrez-le dans un fichier nommé returncode.cetgcc -o returncode returncode.c

ThePiercingPrince
la source
Sur une note semi-connexe: 1) appliquer la licence CC0 sans vous nommer affirmateur n'est probablement pas une bonne idée, et vous devriez le faire comme ça , 2) les programmes aussi petits sont trop triviaux pour que vos droits d'auteur importent, donc vous pourrait aussi bien laisser la licence.
Rhymoid
2
(1) Je ne comprends pas pourquoi vous utilisez à la argv[argc-1]place de argv[1], et pourquoi vous ne vous plaignez pas si argc>2. (2) Je serais enclin à faire le argv[1]test avant le argv[0]test, permettant à l'utilisateur de dire true 56ou false 56au lieu de returncode 56. Vous ne donnez pas de message si argv[0]c'est un mot et argc>0- cela pourrait être déroutant. (3) Je suis obsédé par la convivialité, donc j'utiliserais strcasecmpplutôt strcmp. (4) J'ai tendance à valider les paramètres et à ne pas utiliser la valeur de retour de atoisans la vérifier. Pour un programme de 2 ¢ comme celui-ci, je suppose que cela n'a pas d'importance.
G-Man dit `` Réintègre Monica ''
@ G-Man Ni truene falsetraite aucun argument dans les systèmes Linux
ThePiercingPrince
OK, c'est exact - ils ne traitent aucun argument, y compris argv[0]; les programmes /bin/trueet /bin/falsesont différents exécutables, avec des codes de sortie codés en dur. Vous avez écrit un programme qui peut être installé à la fois true et false(lié), ce qui est intelligent. Mais la question demande comment obtenir un statut de sortie spécifié par l'utilisateur. Votre programme répond à cette question, mais uniquement s'il est installé sous un nom de fichier différent. Tant que vous regardez de argvtoute façon, je pense que le faire de la manière que je suggère maintiendrait le niveau d'intelligence, en évitant d'avoir besoin d'un troisième lien.
G-Man dit `` Réintègre Monica ''
0

Je ne savais pas exactement si votre seul problème avec la exitcommande était sa sortie du shell actuel, mais si c'est le cas, un sous-shell pourrait fonctionner.

username@host$ $(exit 42)
username@host$ echo $?
42

Je l'ai testé sur Cygwin Bash tout à l'heure et cela fonctionne pour moi.

Edit: Désolé d'avoir raté la partie de l'exécuter en dehors d'un contexte shell. Dans ce cas, thsi n'aiderait pas sans l'envelopper dans un .shscript et l'exécuter depuis votre environnement.

Mihir
la source
4
$(exit 42)essaie d'exécuter la sortie de exit 42comme une commande simple qui n'a pas de sens (cela ne fonctionnerait pas non plus yash). (exit 42)(également exécuté exitdans un sous-shell, mais laisse sa sortie seule) aurait plus de sens et serait plus portable (même pour csh ou le shell Bourne).
Stéphane Chazelas