$ 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.
Related
I'm running a bash script using
wget -O - https://myserver/install/Setup.sh | bash
How can I pass a parameter to the above script so it runs? something like
wget -O - https://myserver/install/Setup.sh parameter1 | bash
You can also run your script with:
wget -qO - 'https://myserver/install/Setup.sh' | bash -s parameter1
See: man bash OPTIONS -s
-s If the -s option is present, or if no arguments remain
after option processing, then commands are read from the
standard input. This option allows the positional
parameters to be set when invoking an interactive shell or
when reading input through a pipe.
or alternatively use the -c option.
bash -c "$(wget -qO - 'https://myserver/install/Setup.sh')" '' parameter1
the '' defines the parameter $0 to be empty string. In a normal file based script invocation, the parameter $0 contains the caller script name.
See: man bash OPTIONS -c
-c If the -c option is present, then commands are read from
the first non-option argument command_string. 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.
The standard format for the bash (or sh or similar) command is bash scriptfilename arg1 arg2 .... If you leave off all the first argument (the name or path of the script to run), it reads the script from stdin. Unfortunately, there's no way to leave off the firs argument but pass the others. Fortunately, you can pass /dev/stdin as the first argument and get the same effect (at least on most unix systems):
wget -O - https://myserver/install/Setup.sh | bash /dev/stdin parameter1
If you're on a system that doesn't have /dev/stdin, you might have to look around for an alternative way to specify stdin explicitly (/dev/fd/0 or something like that).
Edit: Léa Gris suggestion of bash -s arg1 arg2 ... is probably a better way to do this.
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.
This question already has answers here:
What does set -e mean in a bash script?
(10 answers)
Closed 6 years ago.
In a bash script what is the use of
set -e
?
I expect it has something to do with environment variables but I have not come across it before
quoting from help set
-e Exit immediately if a command exits with a non-zero status.
i.e the script or shell would exit as soon as it encounters any command that exited with a non-0(failure) exit code.
Any command that fails would result in the shell exiting immediately.
As an example:
Open up a terminal and type the following:
$ set -e
$ grep abcd <<< "abc"
As soon you hit enter after grep command, the shell exits because grep exited with a non-0 status i.e it couldn't find regex abcd in text abc
Note: to unset this behavior use set +e.
man bash says
Exit immediately if a simple command (see SHELL GRAMMAR above) exits with a non-zero
status. The shell does not exit if the command that fails is part of the command list
immediately following a while or until keyword, part of the test in an if statement,
part of a && or ││ list, or if the command’s return value is being inverted via !. A
trap on ERR, if set, is executed before the shell exits.
It is super convenient way to get "fail-fast" behaviour if you want to avoid testing the return code of every command in a bash script.
Suppose there is no file named trumpet in the current directory below script :
#!/bin/bash
# demonstrates set -e
# set -e means exit immediately if a command exited with a non zero status
set -e
ls trumpet #no such file so $? is non-zero, hence the script aborts here
# you still get ls: cannot access trumpet: No such file or directory
echo "some other stuff" # will never be executed.
You may also combine the e with the x option like set -ex where :
-x Print commands and their arguments as they are executed.
This may help you debugging bash scripts.
Reference:Set Manpage
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.
I have a shell script written in bash and this script should take file as an argument,can any one tell me how to write script for this any ideas on this are apprecited
Thanks,
You can access the command line arguments passed to your script using positional parameters.
Also to check if the right number of arguments have been passed to the script, you can make use of the variable $# which holds the number of arguments passed.
if [ $# -eq 1 ]; then
# exactly 1 argument was passed..use it..its available in $1
echo "Argument $1"
else
# either 0 or >1 arguments were passed...error out.
echo "Incorrect number of arguments passed"
exit 1
fi
Sample run:
$ bash a.sh
Incorrect number of arguments passed
$ bash a.sh foo
Argument foo
$ bash a.sh foo bar
Incorrect number of arguments passed
$
If you need to operate on the file, you can take the name of the file as an argument and just use the file with the specified name.
If you just need to read the contents of the file, you can use redirection to have the script read the contents of the file on standard in. You can do this using ./script < inputfile