It's because Bash behaves differently when $0
is sh
.
From Bash's behaviour [Bash Hackers Wiki]:
SH mode
When Bash starts in SH compatiblity mode, it tries to mimic the
startup behaviour of historical versions of sh
as closely
as possible, while conforming to the POSIX® standard as well. The
profile files read are /etc/profile
and
~/.profile
, if it's a login shell.
If it's not a login shell, the environment variable ENV is evaluated and the resulting
filename is taken as name of the startup file.
After the startup files are read, Bash enters the POSIX(r)
compatiblity mode (for running, not for
starting!).
Bash starts in sh
compatiblity
mode when:
- the base filename in
argv[0]
is sh
(Attention dear uber-clever
Linux users… /bin/sh
may be linked to
/bin/bash
, but that doesn't mean it acts like
/bin/bash
)
More information can be found at the Bash Reference Manual: Bash POSIX Mode, specifically:
- Process substitution is not available.
which is why your sh
script is failing, as the <(..)
syntax is using process substitution.
man bash
says:
exec [-cl] [-a name] [command [arguments]]
If command is specified, it replaces the shell. No new process
is created. The arguments become the arguments to command. If
the -l option is supplied, the shell places a dash at the
beginning of the zeroth argument passed to command. This is
what login(1) does. The -c option causes command to be executed
with an empty environment. If -a is supplied, the shell passes
name as the zeroth argument to the executed command. If command
cannot be executed for some reason, a non-interactive shell
exits, unless the execfail shell option is enabled. In that
case, it returns failure. An interactive shell returns failure
if the file cannot be executed. If command is not specified,
any redirections take effect in the current shell, and the
return status is 0. If there is a redirection error, the return
status is 1.
The last two lines are what is important: If you run exec
by itself, without a command, it will simply make the redirections apply to the current shell. You probably know that when you run command > file
, the output of command
is written to file
instead of to your terminal (this is called a redirection). If you run exec > file
instead, then the redirection applies to the entire shell: Any output produced by the shell is written to file
instead of to your terminal. For example here
bash-3.2$ bash
bash-3.2$ exec > file
bash-3.2$ date
bash-3.2$ exit
bash-3.2$ cat file
Thu 18 Sep 2014 23:56:25 CEST
I first start a new bash
shell. Then, in this new shell I run exec > file
, so that all output is redirected to file
. Indeed, after that I run date
but I get no output, because the output is redirected to file
. Then I exit my shell (so that the redirection no longer applies) and I see that file
indeed contains the output of the date
command I ran earlier.
Best Answer
$#
is a special variable inbash
, that expands to the number of arguments (positional parameters) i.e.$1, $2 ...
passed to the script in question or the shell in case of argument directly passed to the shell e.g. inbash -c '...' ....
.This is similar to
argc
in C.Perhaps this will make it clear:
Note that,
bash -c
takes argument after the command following it starting from 0 ($0
; technically, it's justbash
's way of letting you set$0
, not an argument really), so_
is used here just as a placeholder; actual arguments arex
($1
),y
($2
), andz
($3
).Similarly, in your script (assuming
script.sh
) if you have:Then when you do:
the script will output 2; likewise,
will output 1.