Unix return value "-bash" - bash

I do not know what the return value means
grep abc Letters
echo $?
0
echo $0
gives "-bash"
what does "-bash" return value mean

The question should be moved into https://unix.stackexchange.com/.
But anyway, $0 is the value of the variable that holds the name of an application that's executing the command. It's like argv[0] in C.
Example:
cdshines#v3700:~|⇒ echo $0 # I'm in zsh now
/bin/zsh
cdshines#v3700:~|⇒ bash # let's run bash:
cdshines#v3700:~$ echo $0
bash
cdshines#v3700:~$ sh # we need to go deeper:
$ echo $0
sh
$ zsh # deeper!
cdshines#v3700:~|⇒ echo $0
zsh
cdshines#v3700:~|⇒ # hit Ctrl-D (pop one level):
$ echo $0
sh
$ # pop again:
cdshines#v3700:~$ echo $0
bash
cdshines#v3700:~$ exit # again:
cdshines#v3700:~|⇒ echo $0
/bin/zsh # we're at the starting point now

The meaning of $0 is explained in the Bash manual (here, near the bottom of the page):
Expands to the name of the shell or shell script. This is set at shell
initialization. If Bash is invoked with a file of commands (see Shell
Scripts), $0 is set to the name of that file. If Bash is started with
the -c option (see Invoking Bash), then $0 is set to the first
argument after the string to be executed, if one is present.
Otherwise, it is set to the filename used to invoke Bash, as given by
argument zero.
(Unfortunately, it's difficult to search for $0 in the bash manual, since it's listed as just 0.)
By convention, if the current bash process is a login shell, $0 (argv[0] in C terms) is modified, either by bash itself or by the login process, adding a - character to the beginning.
On some systems, /bin/sh is a symbolic link to /bin/bash. If so, and if bash is invoked via the /bin/sh symlink, then $0 will be sh or -sh.
$?, also explained in the Bash manual, "Expands to the exit status of the most recently executed foreground pipeline". More simply, it's the status of the most recently executed command (in your case, grep abc Letters), typically 0 if that command succeeded, or some non-zero value (often, but not always, 1) if it failed.

Return status of the latest shell command is represented by $?:
date
echo $?
0
Here 0 means successful return from previously completed command(date) and any non-zero value means failure status.
When you access $0 is is actually name of the executable in shell and in your case it is -bash since you're running it on the shell.

Related

Bash unexpected value of $0 inside script

I have two files vars.sh and main.sh with the contents:
$ cat vars.sh
#!/bin/bash
fname="$0" # should $0 equal 'vars.sh'?
$ cat main.sh
#!/bin/bash
echo $0
. vars.sh
echo $fname
When I run main.sh I get:
$ ./main.sh
./main.sh
./main.sh
My question is why is $0 inside vars.sh returning main.sh? I read man bash section about $0 but that did not help much.
Sourcing another script involves executing the sourced commands in the current shell. In the current shell, $0 refers to main.sh. You can think of sourcing as similar to "inclusion" or "copy-paste".
However, there does exist a way to get the sourced file name in bash. You can use BASH_SOURCE variable.
If you change vars.sh to:
#!/bin/bash
fname=${BASH_SOURCE[0]}
Then you'll get the sourced file's name as expected.
It is because . (source) includes commands from sourced file, in your case from vars.sh
https://ss64.com/bash/source.html
When a process is started via exec, the first first argument is usually the path to the executable (or whatever the caller decided to pass there as argument). In bash, this argument can be retrieved via $0. In your case, your process is the bash process running main.sh, so that's what is stored there. vars.sh is executed within the same process; hence, $0 is the same.

How to change argv[0] value using bash/shell? [duplicate]

This question already has answers here:
How to change argv[0] value in shell / bash script?
(3 answers)
Closed 3 years ago.
The script should be able to change argv[0] value in shell / bash script.
I found on an older post but couldn't really understand what it is doing.
Could someone please explain how the line works:
sh -c ". '$0'" argv0new "$#"
Also is test ".$INNERCALL" meant to be a variable ?
Original question: How to change argv[0] value in shell / bash script?
#! /bin/bash # try executing this script with several arguments to see the effect
test ".$INNERCALL" = .YES || {
export INNERCALL=YES
# this method works both for shell and bash interpreters
sh -c ". '$0'" argv0new "$#"
# bash -c ". '$0'" argv0new "$#"
exit $?
}
printf "argv[0]=$0\n"
i=1 ; for arg in "$#" ; do printf "argv[$i]=$arg\n" ; i=`expr $i + 1` ;done
The script calls itself with a particular set of parameters when the variable INNERCALL is unset. It sets the variable to avoid an infinite loop, then calls itself in a way which allows the script to set its own $0. The inner instance then executes the code outside of the test, which demonstrates that $0 is now indeed set to a particular value which the script's author chose. When this finishes, we return to the outer instance of the script, which then simply exits.
The real beef is that sh -c 'script...' arg0 arg1 arg2 sets $0 to the first argument after the script itself (arg0 in this example).
Let's go through this line by line
test ".$INNERCALL" = .YES
basically this sees if $INNERCALL already has the value YES in it. Shell's "" function does variable expansion in a safe way, and marshalls it all into a value, e.g.
foo="hello"
bar=", world!"
echo "foobar is: $foo$bar"
prints foobar is: hello, world!
|| {
...
}
This uses the || operator to say, if the previous program returned with a non-zero value (e.g. test is a program that returns 1 if the associated conditional is false), then execute this code block, otherwise skip it (see this link for more on ||)
export INNERCALL=YES
This sets INNERCALL to YES, which means that this only executes for the first level
sh -c ". '$0'" argv0new "$#"
exit $?
This is where the magic happens. sh -c opens a new shell, which then reads its arguments from the string that follows. ". '$0'" uses sh's source function on the value currently at $0, which is supposed to be this current file.
Basically sh -c ". '$0'" just opens the current file again in a child sh process, then the rest of the line replaces the arguments:
argv0new becomes the new $0, and you keep the original arguments to the file by also including "$#"
then exit $? returns whatever return value the child process runs.
the rest of the code is just to prove that all of the arguments except for $0 is the same, and $0 has been replaced.
tl;dr it opens up a child shell process, tells it to read the current file, replaces the arguments, and then exports a test value so that it doesn't infinite loop.
note it doesn't change the value of argv0 but create a new process with argv0new
changing
sh -c ". '$0'" argv0new "$#"
by
exec sh -c ". '$0'" argv0new "$#"
would also work changing bash by sh.
For bash From bash Invoking-Bash
-c
Read and execute commands from the first non-option argument command_string, then exit. If there are arguments after the command_string, the first argument is assigned to $0 and any remaining arguments are assigned to the positional parameters. The assignment to $0 sets the name of the shell, which is used in warning and error messages.

exit of csh shell do not abort the execution immediately

The manual of (t)csh say:
exit [expr]
The shell exits either with the value of the specified expr (an expression, as described under Expressions) or,
without expr, with the value 0.
But if run tcsh -c 'exit 5; echo after exit'; echo $?, we get the following output (test on tcsh of ubuntu/centos and freebsd 10.3):
after exit
0
It seems like the exit command is skipped. How to get the same action like the POSIX/bash shell?
What are you trying to do in a POSIX shell? If you want to print something and exit normally just do printf "%s\n" 'my message'; exit 0. (See https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo for reasons not to use echo)
This is probably a bug in csh that's kept in tcsh for backwards compatibility reasons.
(t)csh is line-oriented in a way that other shells are not and generally processes input a line at a time, for instance
This script:
#!/bin/tcsh -f
exit 5
echo 'after exit'
exits with status 5 when executed.
This script, on the other hand, prints after exit.
#!/bin/tcsh -f
exit 5; echo 'after exit'
Somewhat more surprisingly, a string containing a newline passed to /bin/tcsh is treated like the single-line script, not the multi-line one.
% perl -e 'system("/bin/tcsh -f -c \"exit 5\necho after\"")'

bash -c has different argument semantics ($1 becomes $0!)

$ bash -c 'echo "0 is $0 1 is $1"' abc def
0 is abc 1 is def
$ echo 'echo "0 is $0 1 is $1"' > bashtest
$ bash bashtest abc def
0 is bashtest 1 is abc
The second run is equivalent to if I turned bash test into a shellscript with the shebang and then ran it directly...
Basically I'm wondering why abc isn't always $1. It becomes $0 when run with bash -c.
I also didn't know this. But the man page mentions it:
-c string: If the -c option is present, then commands are read from
string. If there are arguments after the string, they are
assigned to the positional parameters, starting with $0.
The ARGUMENTS section has an even more detailed explanation:
ARGUMENTS
If arguments remain after option processing, and neither the -c nor the
-s option has been supplied, the first argument is assumed to be the
name of a file containing shell commands. If bash is invoked in this
fashion, $0 is set to the name of the file, and the positional parame‐
ters are set to the remaining arguments. Bash reads and executes com‐
mands from this file, then exits. Bash's exit status is the exit sta‐
tus of the last command executed in the script. If no commands are
executed, the exit status is 0. An attempt is first made to open the
file in the current directory, and, if no file is found, then the shell
searches the directories in PATH for the script.

What are the special dollar sign shell variables?

In Bash, there appear to be several variables which hold special, consistently-meaning values. For instance,
./myprogram &; echo $!
will return the PID of the process which backgrounded myprogram. I know of others, such as $? which I think is the current TTY. Are there others?
$1, $2, $3, ... are the positional parameters.
"$#" is an array-like construct of all positional parameters, {$1, $2, $3 ...}.
"$*" is the IFS expansion of all positional parameters, $1 $2 $3 ....
$# is the number of positional parameters.
$- current options set for the shell.
$$ pid of the current shell (not subshell).
$_ most recent parameter (or the abs path of the command to start the current shell immediately after startup).
$IFS is the (input) field separator.
$? is the most recent foreground pipeline exit status.
$! is the PID of the most recent background command.
$0 is the name of the shell or shell script.
Most of the above can be found under Special Parameters in the Bash Reference Manual. Here are all the environment variables set by the shell.
For a comprehensive index, please see the Reference Manual Variable Index.
$_ last argument of last command
$# number of arguments passed to current script
$* / $# list of arguments passed to script as string / delimited list
off the top of my head. Google for bash special variables.
To help understand what $#, $0 and $1, ..., $n do, I use this script:
#!/bin/bash
for ((i=0; i<=$#; i++)); do
echo "parameter $i --> ${!i}"
done
Running it returns a representative output:
$ ./myparams.sh "hello" "how are you" "i am fine"
parameter 0 --> myparams.sh
parameter 1 --> hello
parameter 2 --> how are you
parameter 3 --> i am fine
Take care with some of the examples; $0 may include some leading path as well as the name of the program. Eg save this two line script as ./mytry.sh and the execute it.
#!/bin/bash
echo "parameter 0 --> $0" ; exit 0
Output:
parameter 0 --> ./mytry.sh
This is on a current (year 2016) version of Bash, via Slackware 14.2

Resources