Bash builtins also available as separate executables - bash

I wanted to understand bash builtins. Hence the following questions:
When I think of the term builtin I am thinking that the bash executable has a function defined in its symbol table that other parts of the executable can access without actually having to fork. Is this what builtin means?
I also see that some builtins have a separate executable. For instance type [ returns [ is a shell builtin. But then I also see an executable named /usr/bin/[. Is it correct to say that the same code is available through two executables: one through bash program and another through /usr/bin/[?

Loosely speaking, the program version of the built-ins is used when the shell interpreter is not available or not needed. Let's explain it in more details...
When you run a shell script, the interpreter recognizes the built-ins and will not fork/exec but merely call the function corresponding to the built-in. Even if you call them from an C/C++ executable through system(), the latter launches a shell first and then makes the spawn shell run the built-in.
Here is an example program, which runs echo message thanks to system() library service:
#include <stdlib.h>
int main(void)
{
system("echo message");
return 0;
}
Compile it and run it:
$ gcc msg.c -o msg
$ ./msg
message
Running the latter under strace with the -f option shows the involved processes. The main program is executed:
$ strace -f ./msg
execve("./msg", ["./msg"], 0x7ffef5c99838 /* 58 vars */) = 0
Then, system() triggers a fork() which is actually a clone() system call. The child process#5185 is launched:
clone(child_stack=0x7f7e6d6cbff0, flags=CLONE_VM|CLONE_VFORK|SIGCHLD
strace: Process 5185 attached
<unfinished ...>
The child process executes /bin/sh -c "echo message". The latter shell calls the echo built-in to display the message on the screen (write() system call):
[pid 5185] execve("/bin/sh", ["sh", "-c", "echo message"], 0x7ffdd0fafe28 /* 58 vars */ <unfinished ...>
[...]
[pid 5185] write(1, "message\n", 8message
) = 8
[...]
+++ exited with 0 +++
The program version of the built-ins is useful when you need them from a C/C++ executable without an intermediate shell for the sake of the performances. For instance, when you call them through execv() function.
Here is an example program which does the same thing as the preceding example but with execv() instead of system():
#include <unistd.h>
int main(void)
{
char *av[3];
av[0] = "/bin/echo";
av[1] = "message";
av[2] = NULL;
execv(av[0], av);
return 0;
}
Compile and run it to see that we get the same result:
$ gcc msg1.c -o msg1
$ ./msg1
message
Let's run it under strace to get the details. The output is shorter because no sub-process is involved to execute an intermediate shell. The actual /bin/echo program is executed instead:
$ strace -f ./msg1
execve("./msg1", ["./msg1"], 0x7fffd5b22ec8 /* 58 vars */) = 0
[...]
execve("/bin/echo", ["/bin/echo", "message"], 0x7fff6562fbf8 /* 58 vars */) = 0
[...]
write(1, "message \1\n", 10message
) = 10
[...]
exit_group(0) = ?
+++ exited with 0 +++
Of course, if the program is supposed to do additional things, a simple call to execv() is not sufficient as it overwrites itself by the /bin/echo program. A more elaborated program would fork and execute the latter program but without the need to run an intermediate shell:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
if (fork() == 0) {
char *av[3];
av[0] = "/bin/echo";
av[1] = "message";
av[2] = NULL;
execv(av[0], av);
}
wait(NULL);
// Make additional things before ending
return 0;
}
Compile and run it under strace to see that the intermediate child process executes the /bin/echo program without the need of an intermediate shell:
$ gcc msg2.c -o msg2
$ ./msg2
message
$ strace -f ./msg2
execve("./msg2", ["./msg2"], 0x7ffc11a5e228 /* 58 vars */) = 0
[...]
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDstrace: Process 5703 attached
, child_tidptr=0x7f8e0b6e0810) = 5703
[pid 5703] execve("/bin/echo", ["/bin/echo", "message"], 0x7ffe656a9d08 /* 58 vars */ <unfinished ...>
[...]
[pid 5703] write(1, "message\n", 8message
) = 8
[...]
[pid 5703] +++ exited with 0 +++
<... wait4 resumed>NULL, 0, NULL) = 5703
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5703, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
exit_group(0) = ?
+++ exited with 0 +++

the bash executable has a function defined in its symbol table
There are builtins that are included inside Bash executable. You can load builtins dynamically from a separate shared library on runtime.
can access without actually having to fork
Yes.
Is it correct to say that the same code is available through two executables: one through bash executable and another through /usr/bin/[?
No, it's a different source code. One is a Bash builtin and the other is a program. It will be a different source code. There is also different behavior in grey areas.
$ printf "%q\n" '*'
\*
$ /bin/printf "%q\n" '*'
'*'
$ time echo 1
1
real 0m0.000s
user 0m0.000s
sys 0m0.000s
$ /bin/time echo 1
1
0.00user 0.00system 0:00.00elapsed 50%CPU (0avgtext+0avgdata 2392maxresident)k
64inputs+0outputs (1major+134minor)pagefaults 0swaps
$ [ -v a ]
$ /bin/[ -v a ]
/bin/[: ‘-v’: unary operator expected

Related

Trying to understand behavior of `test <command>` in Bash

Suppose I have this simple C program (test.c):
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
exit (1);
}
Obviously, the exit code of this program is 1:
$ gcc test.c
$ ./a.out
$ echo $?
1
But when I run test ./a.out, the result of the test doesn't match the exit code:
$ test ./a.out
$ echo $?
0
So what is actually being tested? Why is the result of the test 0?
test is a Bash built-in, often invoked by the alternative name [.
The last command (test ./a.out) exits with status 0 indicating success because test ./a.out checks whether ./a.out as a string has one or more characters in it (is not an empty string), and because it isn't an empty string, returns success or 0. The test ./a.out command line does not execute your a.out program — as you could see by printing something from within your program.
As written, your program doesn't need the <stdio.h> header or the arguments to main() — it should be int main(void). You could lose <stdlib.h> too if you use return 1; instead of exit(1);:
int main(void)
{
return 1;
}
To use the exit status in an if condition in the shell, just use it directly:
if ./a.out ; then
echo Success
else
echo Failure
fi
Rule of Thumb: Don't call C programs test because you will be confused sooner or later — usually sooner rather than later.
Your C program returns "1" to the shell (I'd prefer"return()" over exit()", but...)
If you wanted to actually run "a.out" in conjunction with the "*nix" test command, you'd use syntax like:
`./a.out` # classic *nix
or
$(./a.out) # Bash
If you did that, however, "test" would read the value printed to "stdout", and NOT the value returned by your program on exit.
You can read more about test here:
test(1) - Linux man page
The classic test command: Bash hackers wiki
Understanding exit codes and how to use them in Bash scripts
Here is an example:
C program:
#include <stdio.h>
int main (int argc, char *argv[]) {
printf("%d\n", argc);
return 2;
}
Shell script:
echo "Assign RETVAL the return value of a.out:"
./a.out RETVAL=$? echo " " RETVAL=$RETVAL
echo "Assign RETVAL the value printed to stdout by a.out:"
RETVAL=$(./a.out) echo " " RETVAL=$RETVAL
echo "Turn an 'trace' and run a.out with 'test':"
set -x
if [ $(./a.out) -eq 1 ]; then
echo "One"
else
echo "Not One"
fi
Example output:
paulsm#vps2:~$ ./tmp.sh
Assign RETVAL the return value of a.out:
1
RETVAL=2
Assign RETVAL the value printed to stdout by a.out:
RETVAL=1
Turn an 'trace' and run a.out with 'test':
+++ ./a.out
++ '[' 1 -eq 1 ']'
++ echo One
One
ALSO:
A couple of points that have already been mentioned:
a. return 1 is generally a better choice than exit (1).
b. "test" is probably a poor name for your executable - because it collides with the built-in "test" command. Something like "test_return" might be a better choice.

How can interpreter detect being called from a script as opposed to command line?

As "is known", a script my-script-file which starts with
#!/path/to/interpreter -arg1 val1 -arg2 val2
is executed by exec calling /path/to/interpreter with 2(!) arguments:
-arg1 val1 -arg2 val2
my-script-file
(and not, as one might naively expect, with 5 arguments
-arg1
val1
-arg2
val2
my-script-file
as has been explained in many previous questions, e.g.,
https://stackoverflow.com/a/4304187/850781).
My problem is from the POV of an interpreter developer, not script writer.
How do I detect from inside the interpreter executable that I was called from shebang as opposed to the command line?
Then I will be able to decide whether I need to split my first argument
by space to go from "-arg1 val1 -arg2 val2" to ["-arg1", "val1", "-arg2", "val2"] or not.
The main issue here is script files named with spaces in them.
If I always split the 1st argument, I will fail like this:
$ my-interpreter "weird file name with spaces"
my-interpreter: "weird": No such file or directory
On Linux, with GNU libc or musl libc, you can use the aux-vector to distinguish the two cases.
Here is some sample code:
#define _GNU_SOURCE 1
#include <stdio.h>
#include <errno.h>
#include <sys/auxv.h>
#include <sys/stat.h>
int
main (int argc, char* argv[])
{
printf ("argv[0] = %s\n", argv[0]);
/* https://www.gnu.org/software/libc/manual/html_node/Error-Messages.html */
printf ("program_invocation_name = %s\n", program_invocation_name);
/* http://man7.org/linux/man-pages/man3/getauxval.3.html */
printf ("auxv[AT_EXECFN] = %s\n", (const char *) getauxval (AT_EXECFN));
/* Determine whether the last two are the same. */
struct stat statbuf1, statbuf2;
if (stat (program_invocation_name, &statbuf1) >= 0
&& stat ((const char *) getauxval (AT_EXECFN), &statbuf2) >= 0)
printf ("same? %d\n", statbuf1.st_dev == statbuf2.st_dev && statbuf1.st_ino == statbuf2.st_ino);
}
Result for a direct invocation:
$ ./a.out
argv[0] = ./a.out
program_invocation_name = ./a.out
auxv[AT_EXECFN] = ./a.out
same? 1
Result for an invocation through a script that starts with #!/home/bruno/a.out:
$ ./a.script
argv[0] = /home/bruno/a.out
program_invocation_name = /home/bruno/a.out
auxv[AT_EXECFN] = ./a.script
same? 0
This approach is, of course, highly unportable: Only Linux has the getauxv function. And there are surely cases where it does not work well.

shell closes file descriptor num 19

Debugging my application I have found strange behaviour of shell interpreter(/bin/sh on Solaris, /bin/dash in Debian). While fork()ing in shell file descriptor by number 19(dec) is closed by the shell. In my case it leads to closing of communication socket pair between processes.
Looking at shell sources I have found this one and this:
for brevity:
/* used for input and output of shell */
#define INIO 19
and
if (input > 0) {
Ldup(input, INIO);
input = INIO;
}
...
static void
Ldup(int fa, int fb)
{
if (fa >= 0) {
if (fa != fb) {
close(fb);
fcntl(fa, 0, fb); /* normal dup */
close(fa);
}
fcntl(fb, 2, 1); /* autoclose for fb */
}
}
So the netto is simply closing FD number INIO(19);
Simple test for reproducing:
$ exec 19>&1
$ echo aaa >&19
aaa
$ bash -c 'echo aaa >&19'
aaa
$ dash -c 'echo aaa >&19'
dash: 1: Syntax error: Bad fd number
$ ksh -c 'echo aaa >&19'
aaa
The questions are:
What are the reasons for this strange behavior?
What is wrong with file descriptor 19 ?
19 is special because (long ago), the maximum number of open files was 20, e.g.,
#define _NFILE 20
in stdio.h
In POSIX, you may see other symbols such as OPEN_MAX via the sysconf interface.
File descriptors count from 0, and
are normally assigned in increasing order
so the "last possible" file descriptor would have been 19.
If there was an unused file descriptor, making it last would "work".
Both Solaris sh (in particular up through Solaris 10) and dash date back a while, and the detail you noticed probably was not breaking any legacy shell scripts that mattered (much).

Meaning of $? (dollar question mark) in shell scripts

What does
echo $?
mean in shell programming?
This is the exit status of the last executed command.
For example the command true always returns a status of 0 and false always returns a status of 1:
true
echo $? # echoes 0
false
echo $? # echoes 1
From the manual: (acessible by calling man bash in your shell)
?       Expands to the exit status of the most recently executed foreground pipeline.
By convention an exit status of 0 means success, and non-zero return status means failure. Learn more about exit statuses on wikipedia.
There are other special variables like this, as you can see on this online manual: https://www.gnu.org/s/bash/manual/bash.html#Special-Parameters
$? returns the exit value of the last executed command. echo $? prints that value on console. zero implies a successful execution while non-zero values are mapped to various reason for failure.
Hence when scripting; I tend to use the following syntax
if [ $? -eq 0 ]; then
# do something
else
# do something else
fi
The comparison is to be done on equals to 0 or not equals 0.
** Update Based on the comment: Ideally, you should not use the above code block for comparison, refer to #tripleee comments and explanation.
echo $? - Gives the EXIT STATUS of the most recently executed command . This EXIT STATUS would most probably be a number with ZERO implying Success and any NON-ZERO value indicating Failure
? - This is one special parameter/variable in bash.
$? - It gives the value stored in the variable "?".
Some similar special parameters in BASH are 1,2,*,# ( Normally seen in echo command as $1 ,$2 , $* , $# , etc., ) .
It has the last status code (exit value) of a command.
Minimal POSIX C exit status example
To understand $?, you must first understand the concept of process exit status which is defined by POSIX. In Linux:
when a process calls the exit system call, the kernel stores the value passed to the system call (an int) even after the process dies.
The exit system call is called by the exit() ANSI C function, and indirectly when you do return from main.
the process that called the exiting child process (Bash), often with fork + exec, can retrieve the exit status of the child with the wait system call
Consider the Bash code:
$ false
$ echo $?
1
The C "equivalent" is:
false.c
#include <stdlib.h> /* exit */
int main(void) {
exit(1);
}
bash.c
#include <unistd.h> /* execl */
#include <stdlib.h> /* fork */
#include <sys/wait.h> /* wait, WEXITSTATUS */
#include <stdio.h> /* printf */
int main(void) {
if (fork() == 0) {
/* Call false. */
execl("./false", "./false", (char *)NULL);
}
int status;
/* Wait for a child to finish. */
wait(&status);
/* Status encodes multiple fields,
* we need WEXITSTATUS to get the exit status:
* http://stackoverflow.com/questions/3659616/returning-exit-code-from-child
**/
printf("$? = %d\n", WEXITSTATUS(status));
}
Compile and run:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o bash bash.c
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o false false.c
./bash
Output:
$? = 1
In Bash, when you hit enter, a fork + exec + wait happens like above, and bash then sets $? to the exit status of the forked process.
Note: for built-in commands like echo, a process need not be spawned, and Bash just sets $? to 0 to simulate an external process.
Standards and documentation
POSIX 7 2.5.2 "Special Parameters" http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02 :
? Expands to the decimal exit status of the most recent pipeline (see Pipelines).
man bash "Special Parameters":
The shell treats several parameters specially. These parameters may only be referenced; assignment to them is not allowed. [...]
? Expands to the exit status of the most recently executed foreground pipeline.
ANSI C and POSIX then recommend that:
0 means the program was successful
other values: the program failed somehow.
The exact value could indicate the type of failure.
ANSI C does not define the meaning of any vaues, and POSIX specifies values larger than 125: What is the meaning of "POSIX"?
Bash uses exit status for if
In Bash, we often use the exit status $? implicitly to control if statements as in:
if true; then
:
fi
where true is a program that just returns 0.
The above is equivalent to:
true
result=$?
if [ $result = 0 ]; then
:
fi
And in:
if [ 1 = 1 ]; then
:
fi
[ is just an program with a weird name (and Bash built-in that behaves like it), and 1 = 1 ] its arguments, see also: Difference between single and double square brackets in Bash
From http://www.gnu.org/s/bash/manual/bash.html#Special-Parameters
?
Expands to the exit status of the most recently executed foreground pipeline.
See The Bash Manual under 3.4.2 Special Parameters:
? - Expands to the exit status of the most recently executed foreground pipeline.
It is a little hard to find because it is not listed as $? (the variable name is "just" ?). Also see the exit status section, of course ;-)
Happy coding.
Outputs the result of the last executed unix command
0 implies true
1 implies false

What is the $? (dollar question mark) variable in shell scripting? [duplicate]

This question already has answers here:
Meaning of $? (dollar question mark) in shell scripts
(8 answers)
Closed 4 years ago.
I'm trying to learn shell scripting, and I need to understand someone else's code. What is the $? variable hold? I can't Google search the answer because they block punctuation characters.
$? is used to find the return value of the last executed command.
Try the following in the shell:
ls somefile
echo $?
If somefile exists (regardless whether it is a file or directory), you will get the return value thrown by the ls command, which should be 0 (default "success" return value). If it doesn't exist, you should get a number other then 0. The exact number depends on the program.
For many programs you can find the numbers and their meaning in the corresponding man page. These will usually be described as "exit status" and may have their own section.
That is the exit status of the last executed function/program/command. Refer to:
exit / exit status # tldp.org
Special Shell Variables # tldp.org
Special Characters # tlpd.org
A return value of the previously executed process.
10.4 Getting the return value of a program
In bash, the return value of a program is stored in a special variable
called $?.
This illustrates how to capture the return value of a program, I
assume that the directory dada does not exist. (This was also
suggested by mike)
#!/bin/bash
cd /dada &> /dev/null
echo rv: $?
cd $(pwd) &> /dev/null
echo rv: $?
See Bash Programming Manual for more details.
Minimal POSIX C exit status example
To understand $?, you must first understand the concept of process exit status which is defined by POSIX. In Linux:
when a process calls the exit system call, the kernel stores the value passed to the system call (an int) even after the process dies.
The exit system call is called by the exit() ANSI C function, and indirectly when you do return from main.
the process that called the exiting child process (Bash), often with fork + exec, can retrieve the exit status of the child with the wait system call
Consider the Bash code:
$ false
$ echo $?
1
The C "equivalent" is:
false.c
#include <stdlib.h> /* exit */
int main(void) {
exit(1);
}
bash.c
#include <unistd.h> /* execl */
#include <stdlib.h> /* fork */
#include <sys/wait.h> /* wait, WEXITSTATUS */
#include <stdio.h> /* printf */
int main(void) {
if (fork() == 0) {
/* Call false. */
execl("./false", "./false", (char *)NULL);
}
int status;
/* Wait for a child to finish. */
wait(&status);
/* Status encodes multiple fields,
* we need WEXITSTATUS to get the exit status:
* http://stackoverflow.com/questions/3659616/returning-exit-code-from-child
**/
printf("$? = %d\n", WEXITSTATUS(status));
}
Compile and run:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o bash bash.c
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o false false.c
./bash
Output:
$? = 1
In Bash, when you hit enter, a fork + exec + wait happens like above, and bash then sets $? to the exit status of the forked process.
Note: for built-in commands like echo, a process need not be spawned, and Bash just sets $? to 0 to simulate an external process.
Standards and documentation
POSIX 7 2.5.2 "Special Parameters" http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02 :
? Expands to the decimal exit status of the most recent pipeline (see Pipelines).
man bash "Special Parameters":
The shell treats several parameters specially. These parameters may only be referenced; assignment to them is not allowed. [...]
? Expands to the exit status of the most recently executed foreground pipeline.
ANSI C and POSIX then recommend that:
0 means the program was successful
other values: the program failed somehow.
The exact value could indicate the type of failure.
ANSI C does not define the meaning of any vaues, and POSIX specifies values larger than 125: What is the meaning of "POSIX"?
Bash uses exit status for if
In Bash, we often use the exit status $? implicitly to control if statements as in:
if true; then
:
fi
where true is a program that just returns 0.
The above is equivalent to:
true
result=$?
if [ $result = 0 ]; then
:
fi
And in:
if [ 1 = 1 ]; then
:
fi
[ is just an program with a weird name (and Bash built-in that behaves like it), and 1 = 1 ] its arguments, see also: Difference between single and double square brackets in Bash
$? is the result (exit code) of the last executed command.
It is the returned error code of the last executed command. 0 = success
$? is the exit status of a command, such that you can daisy-chain a series of commands.
Example
command1 && command2 && command3
command2 will run if command1's $? yields a success (0) and command3 will execute if $? of command2 will yield a success
The exit code of the last command ran.
It is well suited for debugging in case your script exit if set -e is used. For example, put echo $? after the command that cause it to exit and see the returned error value.

Resources