Linux function return exit

What return/exit values can I use in bash functions/scripts?

The exit status of a process is encoded as a value between 0 and 255, so that’s all you can use as an exit code. If you pass a value outside that range, most shells use the remainder modulo 256. Some shells allow a wider range of integer values for functions.

The only rule for exit codes is that 0 means success and any other value means failure. This rule goes beyond unix: it’s also a common convention on other operating systems (including DOS, Windows, and many embedded systems that have a notion of exit code, but VMS does things differently). In unix systems, it’s baked into the shell’s boolean constructs ( if , while , && , || , ! , set -e , …), into make , and followed by all the standard utilities. In POSIX C programs, EXIT_SUCCESS is 0 and EXIT_FAILURE is some non-zero value (usually 1).

There is no rule (de facto or de jure) regarding the choice of exit codes for failure. Only a few POSIX utilities mandate specific failure status codes:

  • The ! shell operator returns 1 if its operand returns 0. The && and || operator pass the status from the last command.
  • cmp and diff return 1 for different files and ≥2 for error conditions.
  • expr returns 1 if the expression evaluates to zero or null, 2 for an invalid expression, and ≥3 for other errors.
  • grep returns 1 for “not found” and ≥2 for error conditions. Many search commands follow this (but not find , which returns 0 if no file matches).
  • mesg returns 0 for yes, 1 for no, and ≥2 for error.
  • patch returns 1 if a hunk was rejected and ≥2 for other errors.
  • sort -c returns 1 if the file data isn’t sorted and ≥2 for errors.
  • compress and localedef define some small values for specific errors.

There is a common, but not universal idea that larger values mean worse failures. For commands that test a boolean condition such as grep (is this pattern present?) and diff (are these files identical?), 1 means “no” and higher values indicate an error. In addition, values from 126 up are rarely used, as they are baked into the shell (and POSIX commands command , env , nice , nohup and time ):

  • 126 and 127 indicate a failure to invoke an external command;
  • values above 128 in $? indicate a command that was terminated by a signal.

/usr/include/sysexits.h lists some values with their meanings, but it’s from sendmail and I’ve never seen it outside programs that are unrelated to email delivery.

In summary, return 0 for success, and either 1 or 2 for failure. If you need to distinguish between failure cases, start at 1 and increase the value for worse failures.

Источник

How to Safely Exit from Bash Scripts

announcement - icon

The Kubernetes ecosystem is huge and quite complex, so it’s easy to forget about costs when trying out all of the exciting tools.

Читайте также:  Linux bridge ip address

To avoid overspending on your Kubernetes cluster, definitely have a look at the free K8s cost monitoring tool from the automation platform CAST AI. You can view your costs in real time, allocate them, calculate burn rates for projects, spot anomalies or spikes, and get insightful reports you can share with your team.

Connect your cluster and start monitoring your K8s costs right away:

1. Overview

A good Bash script usually performs some checking before executing the commands it contains. For example, a script that executes administrative commands should check if the script is called by the root user or with sudo access. If a less-privileged user calls the script, it should immediately call the exit command to stop its execution. However, is it that simple?

In this tutorial, we will see why the exit command is not the best way of exiting from Bash scripts. Although it is indeed the most straightforward method, unfortunately, it is not the safest one. Here, we’ll explore other ways.

2. Two Methods of Running a Bash Script

One of the common methods of running a Bash script is to execute it from a shell session by calling the script filename prefixed with its filesystem path. For example, to execute a script with the filename of myscript.sh located at the current working directory, we prefix the call with a “./”:

There is another way of running the script, which is sourcing it by prefixing the script call with the source keyword:

Alternatively, we can also use a dot in the place of the source keyword:

2.1. The Difference Between Executing and Sourcing a Bash Script

When we execute a script, Linux spawns a child process of our current shell session and executes myscript.sh in it. Our shell process will block while waiting for the script child process to exit unless it is executed in the background.

Unlike executing a script, sourcing it will not spawn a child process. Instead, the commands inside the scripts will be executed directly in the current process at which the sourcing happens.

First, we’ll create a simple script myscript.sh that simply calls the ps command:

Next, let’s make it executable before executing it:

$ chmod +x myscript.sh $ ./myscript.sh UID PID PPID C STIME TTY TIME CMD mlukman+ 149 148 0 14:27 tty1 00:00:00 -bash mlukman+ 262 149 0 14:45 tty1 00:00:00 /bin/bash ./myscript.sh mlukman+ 263 262 0 14:45 tty1 00:00:00 ps -f

In the above example, the current shell process has a PID of 149. The execution of my script.sh spawned the Bash child process with a PID of 262. That process, in turn, executed the ps command as a child process with a PID of 263.

Now, let’s try sourcing the script:

$ source ./myscript.sh UID PID PPID C STIME TTY TIME CMD mlukman+ 149 148 0 14:27 tty1 00:00:00 -bash mlukman+ 264 149 0 14:59 tty1 00:00:00 ps -f

As we can see, the process for the ps command call was a direct child of the current shell process. There was no intermediate Bash child process of the shell session.

Читайте также:  Установка arch linux bios

2.2. The Behaviour of the exit Command When Executing vs. When Sourcing

Moving on, let’s see what happens if we have an exit command in the script:

#!/bin/bash ps -f exit echo We should not see this 

Executing the script provides the same result as before:

$ ./myscript.sh UID PID PPID C STIME TTY TIME CMD mlukman+ 10 9 1 15:05 tty1 00:00:00 -bash mlukman+ 25 10 0 15:06 tty1 00:00:00 /bin/bash ./myscript.sh mlukman+ 26 25 0 15:06 tty1 00:00:00 ps -f

However, sourcing behaves very differently from before.

The shell session will terminate. Since sourcing a script executes its commands directly in the current process, the exit command in the script terminates the current process, which is the shell session.

The lesson we can take from that little experiment is that the exit command is not always safe to be used inside a Bash script. We’ll need to use some tricks to safely exit from a script.

3. The return Command

To exit from a sourced script, we’ll need a return command instead of the exit command:

#!/bin/bash ps -f return echo We should not see this 

If we source the script, we’ll get the same result as the version of the script with just the ps command:

$ source ./myscript.sh UID PID PPID C STIME TTY TIME CMD mlukman+ 10 9 0 15:05 tty1 00:00:00 -bash mlukman+ 28 10 0 21:09 tty1 00:00:00 ps -f

However, executing it will throw an error:

$ ./myscript.sh UID PID PPID C STIME TTY TIME CMD mlukman+ 10 9 0 15:05 tty1 00:00:00 -bash mlukman+ 29 10 0 21:24 tty1 00:00:00 /bin/bash ./myscript.sh mlukman+ 30 29 0 21:24 tty1 00:00:00 ps -f ./myscript.sh: line 3: return: can only `return' from a function or sourced script We should not see this 

As stated in the error message, the return command is only allowed in a function or sourced script and not allowed when executing a script.

3.1. The returnand-exit Combo

To make the script compatible with both executing and sourcing, we have to combine both the return command and the exit command.

First, we make the return command to not output an error message by piping STDERR to /dev/null:

Next, we add the exit command:

Applying this line into our myscript.sh:

#!/bin/bash ps -f return 2> /dev/null; exit echo We should not see this

Finally, let’s try both executing and sourcing the script:

$ ./myscript.sh UID PID PPID C STIME TTY TIME CMD mlukman+ 10 9 0 15:05 tty1 00:00:00 -bash mlukman+ 35 10 0 21:31 tty1 00:00:00 /bin/bash ./myscript.sh mlukman+ 36 35 0 21:31 tty1 00:00:00 ps -f $ source ./myscript.sh UID PID PPID C STIME TTY TIME CMD mlukman+ 10 9 0 15:05 tty1 00:00:00 -bash mlukman+ 37 10 0 21:31 tty1 00:00:00 ps -f

Voila! Our script now works as intended in both methods of calling. But are we done? Not really.

3.2. Incompatibility With Complex Scripts

Remember the error message that appeared when executing the script with the return command?

./myscript.sh: line 3: return: can only `return' from a function or sourced script

Interestingly, the error message mentioned “function”. Are we not curious to see what will happen if we use the return-and-exit combo method in a function? Let’s move the return-and-exit combo line into a function and call that function instead:

#!/bin/bash exitscript () < return 2>/dev/null; exit > ps -f exitscript echo We should not see this

Next, we’ll see whether or not the script still works:

$ ./myscript.sh UID PID PPID C STIME TTY TIME CMD mlukman+ 10 9 0 15:05 tty1 00:00:00 -bash mlukman+ 40 10 0 21:43 tty1 00:00:00 /bin/bash ./myscript.sh mlukman+ 41 40 0 21:43 tty1 00:00:00 ps -f We should not see this $ source ./myscript.sh UID PID PPID C STIME TTY TIME CMD mlukman+ 10 9 0 15:05 tty1 00:00:00 -bash mlukman+ 42 10 0 21:43 tty1 00:00:00 ps -f We should not see this

Unfortunately, as made apparent from the echoing of the “We should not see this” line, the return-and-exit combo method no longer works if called from inside a function.

Читайте также:  Linux copy newer files

4. The kill Command

The return-and-exit combo trick we explored in the previous sections works in both executing and sourcing methods. However, it fails miserably when used in a function. We need to find a new trick.

The trick we are going to try here seems a little scary. It is the kill command.

In particular, we are going to make the script kill itself. Well, not really. We just make it interrupt itself using the kill command:

The SIGINT option tells the kill command to send an interrupt signal to the PID that is referred to by $$, which is the current process that runs the command. Similar to how CTRL+C works, sending an interrupt signal just stops whatever is still running and queued in the current process, regardless of whether it is the shell session process or its Bash child process.

Let’s implement it into the script, replacing the return-and-exit combo line:

#!/bin/bash exitscript () < kill -SIGINT $$ >ps -f exitscript echo We should not see this

Let’s test the modified script:

$ ./myscript.sh UID PID PPID C STIME TTY TIME CMD mlukman+ 10 9 0 Feb19 tty1 00:00:00 -bash mlukman+ 113 10 0 07:01 tty1 00:00:00 /bin/bash ./myscript.sh mlukman+ 114 113 0 07:01 tty1 00:00:00 ps -f $ source ./myscript.sh UID PID PPID C STIME TTY TIME CMD mlukman+ 10 9 0 Feb19 tty1 00:00:00 -bash mlukman+ 115 10 0 07:01 tty1 00:00:00 ps -f

Apart from the additional empty line, the script now works, showing that the kill command successfully exits the script from within a function. However, a side effect of using this method is that the script execution is stopped abruptly and thus suppresses it from properly returning an exit code. Therefore, we should limit using this method only if we need to exit from within a function and do not need the exit code.

5. Conclusion

In this article, we’ve demonstrated the difference between executing and sourcing a Bash script and how we cannot simply use the exit command to exit the script. We’ve explored two methods: the return-and-exit combo method and the interrupt method. We recommended using the return-and-exit combo method unless we need to exit from within a function, in which case we need to resort to the interrupt method.

Источник

Оцените статью
Adblock
detector