What does /bin/bash $0 $0 do in the 5th line in the following case? - bash

Can someone explain the penultimate line of the following code? I'm trying to understand what does /bin/bash command do in general.
#!/bin/bash
while [ $1 ]
do
shift
/bin/bash $0 $0
done

It is easy to build a reverse-engineering of it to understand what it does by inserting echo before the statement:
#!/bin/bash
while [ "$1" ]
do
shift
echo /bin/bash $0 $0
done
Running it as ./myscript a b c d e f g produces:
/bin/bash ./myscript ./myscript
/bin/bash ./myscript ./myscript
/bin/bash ./myscript ./myscript
/bin/bash ./myscript ./myscript
/bin/bash ./myscript ./myscript
/bin/bash ./myscript ./myscript
/bin/bash ./myscript ./myscript
The /bin/bash just after the echo is printed as-is:
The next two ./myscript.sh are the value of argument $0 which always contains the caller command (here the myscript script).
So for each argument, this script runs itself with itself as argument, using bash.
what does /bin/bash command do in general
/bin/bash is the bash shell interpreter with its full path on disk.
Reading the bash man page man bash should provide extensive answers to your question.

$0 normally returns the name of the script . $1 return parameter passed number one

Related

Empty Positional Argument when using /bin/bash -c <script>

I'm trying to launch a script with /bin/bash -c with positional arguments but can't figure out the following issue:
Suppose I have test.sh as follows:
#!/bin/bash
echo $0
echo $1
> ./test.sh a
./test.sh
a
> /bin/bash -c ./test.sh a
./test.sh
Why does the second one return an empty position argument for $1? Based on the man page:
-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.
It seems like "a" should be assigned to $0 at least, which is not what I saw. /bin/bash -c 'echo $0' a works as expected. Thanks!
The string after -c acts like a miniature script, and the arguments after that are passed to it as $0, $1, $2, etc. For example:
$ bash -c 'echo "\$0=$0, \$1=$1, \$2=$2"' zero one two
$0=zero, $1=one, $2=two
(Note: it's important that the mini-script is in single-quotes; without them the references to $0 would be expanded by your interactive shell before they even get passed to the bash -c command.)
In your case, the mini-script runs another script (./test.sh), but doesn't pass on the arguments. If you wanted to pass them on, you'd do something like this:
$ bash -c './test.sh "$1" "$2"' zero one two
./test.sh
one
If the script had bothered to print its $2 here, it would've gotten "two". It doesn't help to pass on $0, because for a real script that's automatically set to the actual command used to run the script.
bash [long-opt] [-abefhkmnptuvxdBCDHP] [-o option] [-O shopt_option]
-c string [argument ...]
-c supposed to be followed by a string, so you may quote ./test.sh a like:
$ /bin/bash -c "./test.sh a"
./test.sh
a
The -c option does not collect all following arguments of the bash command, but just uses the first non-option argument, which in your case is the one immediately following it. I don't see why you want to use -c here. I would write your command as
/bin/bash test.sh a
Since in this case, no PATH search is involved, you can also omit the ./ part. In fact, test.sh doesn't even need to be executable here.

What are "double sh" calls?

Can you explain what are (as I call it) "double sh calls", how they work and the advantages of such constructs ?
EDIT1 : Maybe an example can help you understand what I'm talking about.
$ set -- --no-resume-playback https://www.facebook.com/TBN/videos/1580372468665943/
$ sh -c 'echo $*'
$
$ sh -c 'echo $*' sh "$*"
--no-resume-playback https://www.facebook.com/TBN/videos/1580372468665943/
I couln't find any example of this in the man :
$ man bash | col -b | egrep "sh -c [\"']"
$
N.B.: If the title of my question is ambiguous, please help me change it because I don't know how to call such shell calls.
man bash describes -c and its arguments:
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, they are assigned to the positional parameters, starting with $0.
In this case, the command_string is echo $*, and the positional arguments are set in this way:
$0 = sh
$1 = "$*" = '--no-resume-playback https://www.facebook.com/TBN/videos/1580372468665943/'

How to pass argument in bash pipe from terminal

i have a bash script show below in a file called test.sh
#!/usr/bin/env bash
echo $1
echo "execution done"
when i execute this script using
Case-1
./test.sh "started"
started
execution done
showing properly
Case-2
If i execute with
bash test.sh "started"
i'm getting the out put as
started
execution done
But i would like to execute this using a cat or wget command with arguments
For example like.
Q1
cat test.sh |bash
Or using a command
Q2
wget -qO - "url contain bash" |bash
So in Q1 and Q2 how do i pass argument
Something simlar to this shown in this github
https://github.com/creationix/nvm
Please refer installation script
$ bash <(curl -Ls url_contains_bash_script) arg1 arg2
Explanation:
$ echo -e 'echo "$1"\necho "done"' >test.sh
$ cat test.sh
echo "$1"
echo "done"
$ bash <(cat test.sh) "hello"
hello
done
$ bash <(echo -e 'echo "$1"\necho "done"') "hello"
hello
done
You don't need to pipe to bash; bash runs as standard in your terminal.
If I have a script and I have to use cat, this is what I'll do:
cat script.sh > file.sh; chmod 755 file.sh; ./file.sh arg1 arg2 arg3
script.sh is the source script. You can replace that call with anything you want.
This has security implications though; just running an arbitrary code in your shell - especially with wget where the code comes from a remote location.

Which process executes script in terminal

when i write ./test1.sh in the MacOS terminal which process executes the script ?
I have installed on my machine oh-my-zsh and thus run zsh in my terminal.
Running test1.sh like so:
./test1.sh
outputs
1 март 2016/ 1 януари 2015/
1 март 2016/
./test3.sh: line 14: declare: -A: invalid option declare: usage: declare [-afFirtx] [-p] [name[=value] ...]
./test3.sh: line 15: януари: syntax error: invalid arithmetic operator (error token is "?нуари")
blah
while running it like this
zsh test3.sh
outputs
1 март 2016/
1 януари 2015/
blah
On the other hand running
declare -A newarray
newarray[януари]="1"
qn="януари"
echo ${newarray[$qn]}
outputs
1
Why is that ?
test3.sh
# backup IFS
SAVEIFS=$IFS
# set IFS to newline
IFS=$(echo -en "\n\b")
# get files
FILES=$(ls -1 -d */)
echo ${FILES}
IFS='\n'
read dirsNameArray <<< ${FILES}
echo ${dirsNameArray[0]}
declare -A monthMap
monthMap['януари']="1"
# monthMap[февруари]="2"
# monthMap[март]="3"
# monthMap[април]="4"
# monthMap[май]="5"
# monthMap[юни]="6"
# monthMap[юли]="7"
# monthMap[август]="8"
# monthMap[септември]="9"
# monthMap[октомври]="10"
# monthMap[ноември]="11"
# monthMap[декември]="12"
# iterate over files
IFS='\n'
for f in $FILES
do
echo "blah"
IFS=' '
# read -r dirNameArray <<< $f
# echo "${monthMap[${dirNameArray[2]}]}"
IFS='\n'
done
# restore $IFS
IFS=$SAVEIFS
You should always include a shebang in your scripts. If you want your shell script to be run by zsh then make sure that the topmost line of you script looks like that:
#!/bin/zsh
This will guarantee that your script will be executed by /bin/zsh (or whatever other executable you specified in the shebang).
If you want to find out what shell is used to execute your script, add the following line to it:
ps ho cmd $$
and see what it prints. If you want to know what shell is used in an interactive session, check if either $BASH_VERSION or $ZSH_VERSION is defined.
Let's find out what shell zsh uses to execute text files:
% echo 'ps -f $$' > script.sh && chmod +x script.sh && ./script.sh
UID PID PPID C STIME TTY STAT TIME CMD
slim 17311 16570 0 15:45 pts/0 S+ 0:00 sh ./script.sh
So it uses sh. That make sense, sh is the lowest common denominator, default shell.
To force a different shell, use #! in the first line of the text file:
% echo '#!/bin/zsh' > script.sh && echo 'ps -f $$' >> script.sh && chmod +x script.sh && ./script.sh
UID PID PPID C STIME TTY STAT TIME CMD
slim 17342 16570 0 15:46 pts/0 S+ 0:00 /bin/zsh ./script.sh
#! is a general purpose mechanism, so you can use it to execute pretty much anything that reads from stdin and ignores "comments" beginning with # -- perl, python, most shells, awk, even things like gnuplot.

ps and /proc/$$/cmdline don't show script arguments without shebang

I've come across a behavior I can't explain. When I run a bash script without a shebang, the ps command will not show the script and its arguments as arguments passed to bash, neither will /proc/$$/cmdline, whereas If I run the script with a shebang, behavior is as expected.
Example with a shebang:
# cat mytest
#!/bin/bash
echo my name is $1
cat /proc/$$/cmdline
echo
ps -p $$ -o args=
# ./mytest John
my name is John
/bin/bash./mytestJohn
/bin/bash ./mytest John
Example without a shebang:
# cat mytest
echo my name is $1
cat /proc/$$/cmdline
echo
ps -p $$ -o args=
# ./mytest John
my name is John
-bash
-bash
In both cases the script will display 'my name is John', but without a shebang I see the bash process without any arguments. How is this possible?

Resources