38

Is there a standard Unix command that does something similar to my example below

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

<cmd here> should be something that can be fork-execed and leaves 56 as the exit code when the process exits. The exit and return shell builtins are unsuitable for what I'm looking for because they affect the invoking shell itself by exiting out of it. <some cmd> should be something that I can execute in non-shell contexts - e.g., invoking from a Python script with subprocess.

E.g., /usr/bin/false always exits immediately with return code 1, but I'd like to control exactly what that return code is. I could achieve the same results by writing my own wrapper script

$ 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

but I'm hoping there happens to exist a standard Unix command that can do this for me.

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
Chris
  • 813
  • 2
  • 7
  • 11
  • 10
    `exit` is the only one I can think of, but it tends to end your shell. `bash -c 'exit 56'` or `bash -c "exit $1"` might work for you. – Tim Kennedy Oct 10 '16 at 03:25
  • @TimKennedy seems to have guessed what my poorly spec-ed out question was trying to ask and answered it. `bash -c 'exit 56'` does what I'd like to do! – Chris Oct 10 '16 at 05:03
  • 13
    How about `(exit 56)`? – cuonglm Oct 10 '16 at 05:19
  • `$python -c 'import sys;sys.exit(int(sys.argv[1]))' 56; $echo $? -> 56` works. – Bakuriu Oct 10 '16 at 08:14
  • 6
    This really sounds like a [XYProblem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem/233676#233676) : what is the ultimate goal of this? Why do you need a command that returns as exit code the number you give it? – Olivier Dulac Oct 10 '16 at 10:21
  • if you need this for job control, for example, you can usually change `any command` into `{ any command ; exit 56 ; }` (or `return 56`, if you don't want to exit the engobing shell) – Olivier Dulac Oct 10 '16 at 10:31
  • Just for the record, here is the [dual question](http://stackoverflow.com/questions/26110767/unix-utility-which-executes-a-command-and-tests-its-exit-status) – Michaël Le Barbier Oct 10 '16 at 14:44
  • 1
    Well, you have the `true` and `false` built-ins if you need to return 0 or 1. – gardenhead Oct 10 '16 at 16:19
  • or `\`exit 56\``. – webb Oct 10 '16 at 22:39
  • 1
    related: (non portable) smallest program to return a compile-time fixed value: http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html – Florian Castellane Oct 11 '16 at 12:45

5 Answers5

46
  1. A return based function would work, and avoids the need to open and close another shell, (as per Tim Kennedy's comment):

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

    Output:

    56
    
  2. using exit in a subshell:

    (exit 56)
    

    With shells other than ksh93, that implies forking an extra process so is less efficient than the above.

  3. bash/zsh/ksh93 only trick:

    . <( echo return 56 )
    

    (that also implies an extra process (and IPC with a pipe)).

  4. zsh's lambda functions:

    (){return 56}
    
agc
  • 7,045
  • 3
  • 23
  • 53
  • Good thought. Unfortunately, my original question was not defined well enough - needing to "open and close another shell" was almost a requirement of what I wanted to do. I edited my question to require that `` is an execve()-able thing. – Chris Oct 10 '16 at 05:06
  • 2
    @Chris: For any shell command you can make it execve()able by converting it to `/bin/sh -c ...originalcommand...` – psmears Oct 10 '16 at 10:02
  • 3
    I am mildly disappointed that ?=56 doesn't work. – Joshua Oct 10 '16 at 18:41
  • @Joshua: maybe it does? but then this affectation was successful, and you have $? set to 0 ... ;) – Olivier Dulac Oct 11 '16 at 10:35
  • @OlivierDulac: It doesn't. It spits out a parse error. – Joshua Oct 11 '16 at 15:11
  • @joshua: i was just joking to point out that even if it did work, it wouldnt set $? to the value 56 ^^ – Olivier Dulac Oct 11 '16 at 15:13
18

There is no standard UNIX command for just returning a specific value. The GNU Core Utilies provide true and false only.

However, you can easily implement this yourself as ret:

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

Compile:

cc ret.c -o ret

And run:

./ret 56 ; echo $?

Prints:

56

If you need this to work everywhere (where bash is available, that is) without installing any extra tools, you probably need to resort to the following command as @TimKennedy suggested in comments:

bash -c 'exit 56'

Note that the valid range of return values is 0..255 inclusive.

tmh
  • 393
  • 1
  • 2
  • 7
  • 3
    `true` and `false` are in Unix (and even POSIX) as well, not only in GNU. – Stéphane Chazelas Oct 10 '16 at 16:52
  • @StéphaneChazelas Thanks for pointing that out. As the OP mentioned bash, I somehow assumed GNU -- even though the question itself states 'Unix'. – tmh Oct 10 '16 at 16:56
14

If you need the exit status to be set by an executed commands. There is no dedicated command for that1, but you can use the interpreter of any language that has the capability to exit with an arbitrary exit status. sh is the most obvious one:

sh -c 'exit 56'

With most sh implementations, that's limited to exit codes 0 to 255 (sh will accept greater values but may truncate it or even cause a signal to be sent to the process executing sh like with ksh93 for codes 257 to 320).

An exit code can be any integer (int) value but note that you need to retrieve it with the waitid() interface so the value is not truncated to 8 bits (on Linux, it's still truncated with waitid() as well though). Hence why it's rare (and not a good idea) to use exit codes above 255 (use 0-123 for normal operation).

Alternatively:

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

(those don't truncate the exit code to 8 bits).

With NetBSD find, you can do:

find / -exit 56

1exit is the standard command to do that but being a shell special builtin, there's no requirement that there also be a command by that name in the file system like for regular built-ins, and most systems won't include one

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
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]);
}

Save this in a file named returncode.c and gcc -o returncode returncode.c

  • On a semi-related note: 1) applying the CC0 license without naming yourself as an affirmer is probably not a good idea, and you should do it [like this](https://wiki.creativecommons.org/wiki/CC0_FAQ#May_I_apply_CC0_to_computer_software.3F_If_so.2C_is_there_a_recommended_implementation.3F), 2) programs this small are too trivial for your copyright to matter, so you might as well leave out the license. – Rhymoid Oct 10 '16 at 16:53
  • 2
    (1) I don’t understand why you’re using `argv[argc-1]` instead of `argv[1]`, and why you’re not complaining if `argc>2`. (2) I’d be inclined to do the `argv[1]` test before the `argv[0]` test, allowing the user to say `true 56` or `false 56` instead of `returncode 56`. You don’t give a message if `argv[0]` is a word and `argc>0` — that could be confusing. (3) I’m obsessed with user-friendliness, so I’d use `strcasecmp` instead of `strcmp`. (4) I tend to validate parameters, and not use the return value of `atoi` without verifying it. For a 2¢ program like this, I guess it doesn’t matter. – G-Man Says 'Reinstate Monica' Oct 10 '16 at 21:41
  • @G-Man Neither `true` nor `false` processes any argument in Linux systems – ThePiercingPrince Oct 11 '16 at 07:16
  • OK, that’s correct — they don’t processes any argument, including `argv[0]`; the programs `/bin/true` and `/bin/false` are different executables, with hard-coded exit codes. You’ve written a program that can be installed as ***both*** `true` and `false` (linked), which is clever. But the question asks how to get a user-specified exit status. Your program answers that question, but only if it is installed under a different filename. As long as you’re looking at `argv` anyway, I believe that doing it the way I suggest would maintain the level of cleverness, by avoiding the need for a third link. – G-Man Says 'Reinstate Monica' Oct 11 '16 at 21:18
0

I wasn't exactly sure if your only problem with the exit command was it exiting out of the current shell, but if so, a subshell might work.

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

I tested this on Cygwin Bash just now and it works for me.

Edit: Sorry I missed the part of running it outside a shell context. In that case thsi wouldn't help without wrapping it within a .sh script and executing that from your environment.

Mihir
  • 1
  • 4
    `$(exit 42)` tries to execute the output of `exit 42` as a simple command which makes little sense (it also wouldn't work with `yash`). `(exit 42)` (also run `exit` in a subshell, but leaves its output alone) would make more sense and would be more portable (even to csh or the Bourne shell). – Stéphane Chazelas Oct 10 '16 at 16:56