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
Related
In bash, this syntax can be used to get list of command line arguments, starting from $2:
echo "${#:2}"
This syntax does not seem to work in sh (/bin/dash).
What would be the best way to emulate this in sh ?
(shift; echo "$#")
Using the subshell created by the parens ensures that "$#" in the outer scope is not modified.
As another approach (uglier, but avoiding the need for a subshell), you can remove the first argument, and re-add it later:
argOne=$1 # put $1 in $argOne
shift # rename $2 to $1, $3 to $2, etc
echo "$#" # Pass the new argument list to echo
set -- "$argOne" "$#" # Create a new argument list, restoring argOne before the rest
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.
I have she script as the below content
chr=$0
start=$1
end=$2
echo -e "$chr\t$start\t$end" > covdb_input.bed
How do i pass the chr,Start and end variables in to echo command.. or write same to file "covdb_input.bed" with TAB sep as in echo command.
You're doing everything right, except that you probably initialize your variables with the wrong things.
I'm assuming you get arguments for the script (or shell function), and that you want to use these. Then pick the positional variables from $1 and onwards as $0 will usually contain the name of the current shell script or shell function.
Also, you might find people scoffing about the use of -e with echo (it's a common but non-standard option). Instead of using echo you could use printf like this:
printf "%s\t%s\t%s" "$chr" "$start" "$end" >myfile.bed
Or just
printf "$chr\t$start\t$end" >myfile.bed
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 many scripts in a directory that all start with deploy_ (for instance, deploy_example.com).
I usually run them one at a time by calling ./deploy_example.com.
How do I run them all, one after the other (or all at once if possible...)?
I've tried:
find deploy_* | xargs | bash
But that fails as it needs the absolute path if called like that.
You can do it in several ways. For example, you can do:
for i in deploy_* ; do bash $i ; done
You can simply do:
for x in deploy*; do bash ./$x; done
Performed in a subshell to prevent your current IFS and positional parameters from being lost.
( set -- ./deploy_*; IFS=';'; eval "$*" )
EDIT: That sequence broken down
( # start a subshell, a child process of your current shell
set -- ./deploy_* # set the positional parameters ($1,$2,...)
# to hold your filenames
IFS=';' # set the Internal Field Separator
echo "$*" # "$*" (with the double quotes) forms a new string:
# "$1c$2c$3c$4c..."
# joining the positional parameters with 'c',
# the first character of $IFS
eval "$*" # this evaluates that string as a command, for example:
# ./deploy_this;./deploy_that;./deploy_example.com
)
find deploy_* | xargs -n 1 bash -c
Will run them all one after the other. Look at the man page for xargs and the --max-procs setting to get some degree of parallelism.