are there any benefits of ending a bash script with exit - bash

I encountered a bash script ending with the exit line. Would anything changes (save scaring users who 'source' rather than calling straight when the terminal closes )?
Note that I am not particularly interested in difference between exit and return. Here I am only interested in differences between having exit without parameters in the end of a bash script (one being closing console or process which sources the script rather than calling).
Could it be to address some less known shell dialects?

There are generally no benefits to doing this. There are only downsides, specifically the inability to source scripts like you say.
You can construct scenarios where it matters, such as having a sourceing script rely on it for termination on errors, or having a self-extracting archive header avoid executing its payload, but these unusual cases should not be the basis for a general guideline.

The one significant advantage is that it gives you explicit control over the return code.
Otherwise the return code of the script is going to be the return code of whatever the last command it executed happened to be. Which may or may not be indicative of the actual success or failure of the script as a whole.
A slightly less significant advantage is that if the last command's exit code is significant, and you follow it up with "exit $?" that tells the maintenance programmer coming along later that yes, you did consider what the exit code of the program should be and he shouldn't monkey with it without understanding why.
Conversely, of course, I wouldn't recommend ending a bash script with an explicit call to exit unless you really mean "ignore all previous exit codes and use this one". Because that's what anyone else looking at your code is going to assume you wanted and they're going to be annoyed that you wasted their time trying to figure out why if you did it just by rote and not for a reason.

Related

Does bash support exception?

Is it possible to raise exceptions in bash? This can be useful, for example, when we want the script to exit when an error happens in a subcommand. Without exception, it seems the best we can do is to append || exit after each subcommand, which gives poor readability.
I didn't find descriptions about exceptions in bash manual. But I'm wondering whether there are ways to simulate them.
No, Bash does not have a notion of exceptions like other languages like Java have. The key unit of error-reporting in Bash is the exit code; functions, commands, and scripts all return 0 on success and non-zero to report some sort of error condition. Many programs document specific exit codes to report certain failure modes, for instance grep uses 1 to mean no-match-found and 2 to report other errors.
There are a number of useful debugging tricks you can take advantage of despite the lack of exceptions, including the caller command which enables some introspection of the current execution context.
Other resources:
How to debug a bash script?
Trace of executed programs called by bash script
Accessing function call stack in trap function

Exit command examples

I want to press a key at any point, causing the simulation to stop without loosing data collected until that point. I don't know how to do the exit command. Can you give me some examples?
I think, WandMaker's comment tells only half of the story.
First, there is no general rule, that Control-C will interrupt your program (see the for instance here), but assume that this works in your case (since it will work in many cases):
If I understand you write, you want to somehow "process" the data collected up to this point. This means that you need to intercept the effect of Control-C (which, IF it works as expected, will make the controlling shell deliver a SIGINT), or that you need to interecept the "exit" (since the default behaviour upon receiving a SIGINT would be to exit the program).
If you want to go along the first path, you need to catch the Interrupt exception; see for example here.
If you want to follow the second route, you need to install an exit handler. Note that it will be called too when the program is exited in the normal way.
If you are unsure, which way is better - and I see no general way to recommend one over the other -, try the first one. There is less chance that you will accidentally ruin something.

why shell uses 0 as success?

In C and many other languages, 0 means false and 1 ( or non-zero )
means true. In the shell 0 for the status of a process means success
and non-zero means an error. Shell if statements essentially use 0
for true. Why did the writer of the first shell decide to use 0 for
true?
The OP asking about the
Why did the writer of the first shell decide to use 0 for true?
The short answer:
Isn't possible to answer this question in this form,
because the first shell has not any exit status.
EDIT
Because #John Kugelman asked for summarisation, this answer getting a bit long.
Nor the second, third and Nth shell hasn't anything that could be called "the exit status". For the approximative correct answer, need to go to deeper into the history.
The shell was invented
by "Louis Pouzin" as the "RUNCOM" tool, in
the MIT's Compatible Time-Sharing System
read about CTSS,
where is written:
Louis Pouzin also invented RUNCOM for CTSS. This facility, the
direct ancestor of the Unix shell script, allowed users to create
a file-system file of commands to be executed, with parameter
substitution. Louis also produced a design for the Multics shell,
ancestor of the Unix shell.
Read also this page.
The "next level" was in the Multics, called as
the Multics Command Language:
The command language interpreter, the Shell, is normally driven by
the Listener.
Its function is to listen for requests in the form of command lines
typed in at the user console. In the above command language
description, the listener reads in a line from the console, evaluates
the line as a command, and re-calls itself to repeat the function.
It hasn't anything that could be called "exit status".
The programs calls the terminate{file/process} procedure,
what stops the program execution.
And this is one of the most important points. Need differentiate between
the exit as system call
When you terminating a running (compiled) program, (for example, in "C" using the exit(N)) in the reality you call
a library function, what makes the bridge to the underlying operating system and correctly terminates the program.
This means the "exit(N)" (from the point of view of the exit function) is OS implementation-dependent.
See Wikipedia
shell's exit status -
The exit status of an executed command is the value returned by the waitpid system call or equivalent function.
Wikipedia. The waitpid like functions usually returns the exit status from the previous point, but it is OS implementation-dependent. (ofc, now standardized by POSIX).
Back to the history:
After the Multics, the UNIX got developed. From this page
the shell as we know today has two predecessors:
the Thompson shell (version 1 up to version 6) - if someone is interested, here is maintained a runnable version for the v6.
and the Programmers Work Bench [PWB] shell
the first shell that we known today was the Bourne shell V7
If interested, read through the manuals - links are here.
The first mentioning of the "exit code" is in the
Manual of the PWB (aka Mashey) Shell.
All shells "before" talks only about the:
Termination Reporting
If a command (not followed by "&") terminates abnormally,
a message is printed. (All terminations other than exit
and interrupt are considered abnormal.)
So, the answer to the question is in the above lines, - in line what is already said in the comments:
Because read the exit code as an error code. 0-mean no error. >0 some error.
Probably because there's only one mode of success and many modes of failure.
Nice citation from the PWB shell manual
$r is the exit status code of the preceding command.
``0'' is the normal return from most commands.
Notice the: most word. :), e.g. it was not invented in "one moment" by someone, but it is evolved in time. And it very tightly depends on the implementation of the exit and wait-like calls in the underlaying OS.
For example, the 1st version of UNIX know nothing about the exit(N) levels - it was a simple exit() and the wait didn't return a specific value - from the The first edition of the Unix Programmer's Manual, dated November 3, 1971
exit is the normal means of terminating a process. All files are
closed and the parent process is notified if it is executing a wait.
wait causes its caller to delay until one of its child processes terminates. If any child has already died, return is immediate; if
there are no children, return is immediate with the error bit set.
Also, read here - really worth - The Evolution of the Unix Time-sharing System - about the evolution of the fork, exec, exit and wait...
The V7 standardized the exit status as:
The value of a simple command is its
exit status if it terminates normally or 200+status if it
terminates abnormally (see signal(2) for a list of status
values).
also:
DIAGNOSTICS
Errors detected by the shell, such as syntax errors cause
the shell to return a non-zero exit status. If the shell is
being used non interactively then execution of the shell
file is abandoned. Otherwise, the shell returns the exit
status of the last command executed (see also exit).
Howg.. OMG... Someone could be so nice as to edit this post and correct my broken English and/or extend/correct the above...
And finally - sorry folks - totally of the topic - but can't resist adding the following image from the history - not many current users know Microsoft-XENIX. ;)
In most processors, the processor status indicates if the last instruction produced a zero value. One can branch if zero (or nonzero) without explicitly comparing with zero (saving instruction cycles). If you make zero success, that gives multiple ways to return failure (and save instructions).
This is actually a very poor convention. The VMS operating system from the 70's remains more advanced then nearly every over system in use today. It had a centralized error system in which applications could add their own error messages and codes which would be integrated with system error codes. In the VMS system, odd values were success and even values were failures.
Let's say you had a function that opens a file. You could have one success code that says it opened an existing file and another that says it created a new file.

BASH shell process control - any other examples of controlling/scheduling work

I've inherited a medium sized project in which the main (batch) program is fed work through a large set of shell scripts that do a lot of process control (waiting for process to complete, sleeping, checking for conditions, etc) [ and reprocessed through perl scripts ]
Are there other examples of process control by shell scripts ? I would like to see what other people have done as a comparison. (as i'm not really fond of the 6,668 line shell script)
It may lead to that the current program works and doesn't need to be messed with or for maintenance reasons - it's too cumbersome and doing it another way will be easier to maintain, but I need other examples.
To reduce the "generality" of the question here's an example of what I'm looking for: procsup
Inquisitor project relies on process control from shell scripts extensively. You might want to see it's directory with main function set or directory with tests (i.e. slave processes) that it runs.
This is quite general question, and therefore giving specific answers may be a little bit difficult. (And you wont be happy with 5000 lines long example.) Most probably architecture of your application is faulty, and requires rather complete rework.
As you probably already know, process control with bash is pretty simple:
./test_script.sh &
test_script_pid=$!
wait $test_script_pid # waits until it's done
./test_script2.sh
echo $? # Prints return code of previous command
You can do same things with for example Python subprocess (or with Perl, obviously). If you have complex architecture with large number of different programs, then process is obviously non-trivial.
That is an awfully bug shell script. Have you considered refactoring it?
From the sound of it, there may be a lot of instances where you could replace several lines of code with a call to a shell function. If you can simplify the code in this way, then it will be easier to see where there are errors in the logic.
I've used this tactic successfully with a humongous PERL script and it turned out to have some serious logic errors and to be a security risk because it had embedded passwords that were obfuscated in an easily reversible way. The passwords that were exposed could have been used by persons unknown (well, a disgruntled employee) to shut down an entire global network.
Some managers were leaning towards making a security exception because this script was so important, but when the logic error was explained and it was clear that this script was providing incorrect data, it was decided that no data was better than dirty data. The guy who wrote that script taught himself programming with a PERL book and the writing of the script.

Pitfalls of using shell scripts to wrap a program?

Consider I have a program that needs an environment set. It is in Perl and I want to modify the environment (to search for libraries a special spot).
Every time I mess with the the standard way to do things in UNIX I pay a heavy price and I pay a penalty in flexibility.
I know that by using a simple shell script I will inject an additional process into the process tree. Any process accessing its own process tree might be thrown for a little bit of a loop.
Anything recursive to a nontrivial way would need to defend against multiple expansions of the environment.
Anything resembling being in a pipe of programs (or closing and opening STDIN, STDOUT, or STDERR) is my biggest area of concern.
What am I doing to myself?
What am I doing to myself?
Getting yourself all het up over nothing?
Wrapping a program in a shell script in order to set up the environment is actually quite standard and the risk is pretty minimal unless you're trying to do something really weird.
If you're really concerned about having one more process around — and UNIX processes are very cheap, by design — then use the exec keyword, which instead of forking a new process, simply exec's a new executable in place of the current one. So, where you might have had
#!/bin/bash -
FOO=hello
PATH=/my/special/path:${PATH}
perl myprog.pl
You'd just say
#!/bin/bash -
FOO=hello
PATH=/my/special/path:${PATH}
exec perl myprog.pl
and the spare process goes away.
This trick, however, is almost never worth the bother; the one counter-example is that if you can't change your default shell, it's useful to say
$ exec zsh
in place of just running the shell, because then you get the expected behavior for process control and so forth.

Resources