Shell Function Does Not Return Values Greater Than 255

Could the shell function return value not exceed 255?

If you look at man bash and search for EXIT STATUS you will find the following explanation:

EXIT STATUS
The exit status of an executed command is the value returned by the waitpid system call or equivalent
function. Exit statuses fall between 0 and 255, though, as explained below, the shell may use values
above 125 specially. Exit statuses from shell builtins and compound commands are also limited to this
range. Under certain circumstances, the shell will use special values to indicate specific failure modes.

For the shell's purposes, a command which exits with a zero exit status has succeeded. An exit status of
zero indicates success. A non-zero exit status indicates failure. When a command terminates on a fatal
signal N, bash uses the value of 128+N as the exit status.

If a command is not found, the child process created to execute it returns a status of 127. If a command
is found but is not executable, the return status is 126.

If a command fails because of an error during expansion or redirection, the exit status is greater than
zero.

Shell builtin commands return a status of 0 (true) if successful, and non-zero (false) if an error occurs
while they execute. All builtins return an exit status of 2 to indicate incorrect usage, generally
invalid options or missing arguments.

Bash itself returns the exit status of the last command executed, unless a syntax error occurs, in which
case it exits with a non-zero value. See also the exit builtin command below.

If you really want to return values greater than 125, you can use echo instead of return like this:

#!/usr/bin/env bash
my_function () {
echo 256
}
retval=$( my_function )
echo $retval

Number greater than 255 in bash shell script

In bash, return does not do what you expect it does from other programming languages:

The return value of a simple command is its exit status, or 128+n if
the command is terminated by signal n.

This code will work:

#!/bin/bash

function returnnumber(){
echo $1
}

returnnumber 256

Bash function can't return large numbers

Return values of Unix processes (and a shell function acts like one) can be only in the range of a single byte, i. e. 0…255 (in some contexts the range is -128…+127).

To return values in larger ranges I propose to use the stdout as a channel to provide the result:

function getqsubnumber {
# Return how many simulations ($qsubnumber) are currently running
qstat | grep p00 | wc -l
}

qs=$(getqsubnumber)

ExitCodes bigger than 255, possible?

Using wait() or waitpid()

It is not possible on Unix and derivatives using POSIX functions like wait() and waitpid(). The exit status information returned consists of two 8-bit fields, one containing the exit status, and the other containing information about the cause of death (0 implying orderly exit under program control, other values indicating that a signal killed it, and indicating whether a core was dumped).

Using sigaction() with SA_SIGINFO

If you work hard, and read the POSIX specification of sigaction() and <signal.h> and Signal Actions, you will find that you can get hold of the 32-bit value passed to exit() by a child process. However, it is not completely straight-forward.

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

static siginfo_t sig_info = { 0 };
static volatile sig_atomic_t sig_num = 0;
static void *sig_ctxt = 0;

static void catcher(int signum, siginfo_t *info, void *vp)
{
sig_num = signum;
sig_info = *info;
sig_ctxt = vp;
}

static void set_handler(int signum)
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = catcher;
sigemptyset(&sa.sa_mask);

if (sigaction(signum, &sa, 0) != 0)
{
int errnum = errno;
fprintf(stderr, "Failed to set signal handler (%d: %s)\n", errnum, strerror(errnum));
exit(1);
}
}

static void prt_interrupt(FILE *fp)
{
if (sig_num != 0)
{
fprintf(fp, "Signal %d from PID %d (status 0x%.8X; UID %d)\n",
sig_info.si_signo, (int)sig_info.si_pid, sig_info.si_status,
(int)sig_info.si_uid);
sig_num = 0;
}
}

static void five_kids(void)
{
const int base = 0xCC00FF40;
for (int i = 0; i < 5; i++)
{
pid_t pid = fork();
if (pid < 0)
break;
else if (pid == 0)
{
printf("PID %d - exiting with status %d (0x%.8X)\n",
(int)getpid(), base + i, base + i);
exit(base + i);
}
else
{
int status = 0;
pid_t corpse = wait(&status);
if (corpse != -1)
printf("Child: %d; Corpse: %d; Status = 0x%.4X - waited\n", pid, corpse, (status & 0xFFFF));
struct timespec nap = { .tv_sec = 0, .tv_nsec = 1000000 }; // 1 millisecond
nanosleep(&nap, 0);
prt_interrupt(stdout);
fflush(0);
}
}
}

int main(void)
{
set_handler(SIGCHLD);
five_kids();
}

When run (program sigexit73 compiled from sigexit73.c), this produces output like:

$ sigexit73
PID 26599 - exiting with status -872349888 (0xCC00FF40)
Signal 20 from PID 26599 (status 0xCC00FF40; UID 501)
Child: 26600; Corpse: 26599; Status = 0x4000 - waited
PID 26600 - exiting with status -872349887 (0xCC00FF41)
Signal 20 from PID 26600 (status 0xCC00FF41; UID 501)
Child: 26601; Corpse: 26600; Status = 0x4100 - waited
PID 26601 - exiting with status -872349886 (0xCC00FF42)
Signal 20 from PID 26601 (status 0xCC00FF42; UID 501)
Child: 26602; Corpse: 26601; Status = 0x4200 - waited
PID 26602 - exiting with status -872349885 (0xCC00FF43)
Signal 20 from PID 26602 (status 0xCC00FF43; UID 501)
Child: 26603; Corpse: 26602; Status = 0x4300 - waited
PID 26603 - exiting with status -872349884 (0xCC00FF44)
Signal 20 from PID 26603 (status 0xCC00FF44; UID 501)
$

With the one millisecond call to nanosleep() removed, the output is apt to look like:

$ sigexit73
sigexit23
PID 26621 - exiting with status -872349888 (0xCC00FF40)
Signal 20 from PID 26621 (status 0xCC00FF40; UID 501)
Child: 26622; Corpse: 26621; Status = 0x4000 - waited
PID 26622 - exiting with status -872349887 (0xCC00FF41)
PID 26623 - exiting with status -872349886 (0xCC00FF42)
Signal 20 from PID 26622 (status 0xCC00FF41; UID 501)
Child: 26624; Corpse: 26623; Status = 0x4200 - waited
Signal 20 from PID 26623 (status 0xCC00FF42; UID 501)
Child: 26625; Corpse: 26622; Status = 0x4100 - waited
PID 26624 - exiting with status -872349885 (0xCC00FF43)
PID 26625 - exiting with status -872349884 (0xCC00FF44)
$

Note that there are only three lines starting Signal here, and also only three lines ending waited; some of the signals and exit statuses are lost. This is likely to be because of timing issues between the SIGCHLD signals being set to the parent process.

However, the key point is that 4 bytes of data can be transmitted in the exit() status when the code uses sigaction(), SIGCHLD, SA_SIGINFO to track the status.

Just for the record, the testing was performed on a MacBook Pro running macOS Mojave 10.14.6, using GCC 9.2.0 and XCode 11.3.1. The code is also available in my SOQ (Stack Overflow Questions) repository on GitHub as file sigexit73.c in the src/so-1843-7779 sub-directory.

Why don't I get the return of my function?

From the comments :

  • Replace return with echo and your code works fine. - Blender

  • The $(...) syntax is specifically designed to give you the output of a command, even if that command happens to be a function call. return in a function is similar to exit for the script as a whole; it sets its status, which is an integer in the range 0 to 255. (This is quite different from other languages you might be used to, where return is used to return a value from a function.) – Keith Thompson

  • Bash functions are not like functions in other languages. They behave the same as any other command: they can take command line arguments, read from standard input, write to standard output and standard error, and return with an exit status. They don't--strictly speaking--return a computed value. – chepner

Why is my shell only detecting return codes between 0 - 255?

C++ allows you to return two values from main: EXIT_SUCCESS and EXIT_FAILURE. Returning EXIT_SUCCESS is the same as returning 0, which has the same effect as calling std::exit(0).

The return value is passed back to the operating system in an implementation-defined manner, and what the OS does with the value is also implementation-defined. Finally, the semantics of returning any other value from main are implementation-defined, too.

C++ has nothing more to say on this subject. You need to consult your platform's documentation.

Why is there no effect when using return between parentheses in shell function?

Clearly the fff reaches till its end and that's why 1 is being printed. The point is that return is executed in a subshell as you have placed it inside parenthesis. You should have done

true && return

instead of

true && (return)

Anything placed inside () is executed using a subshell and after the commands in the subshell finishes the control is passed back to the function fff and the subsequent command echo 1 is executed in this case.

Below script will give you the right output

#!/bin/bash
function fff(){
true && return 255
echo 1
}
fff
echo $? # Will give you 255 as output

Mind that return value greater than 255 will be wrapped around ie you will get (value modulo 255) - 1. For example if you return 259 you will get 3 ie (259%255)-1.



Related Topics



Leave a reply



Submit