Propagate all arguments in a Bash shell script - bash

I am writing a very simple script that calls another script, and I need to propagate the parameters from my current script to the script I am executing.
For instance, my script name is foo.sh and calls bar.sh.
foo.sh:
bar $1 $2 $3 $4
How can I do this without explicitly specifying each parameter?

Use "$#" instead of plain $# if you actually wish your parameters to be passed the same.
Observe:
$ cat no_quotes.sh
#!/bin/bash
echo_args.sh $#
$ cat quotes.sh
#!/bin/bash
echo_args.sh "$#"
$ cat echo_args.sh
#!/bin/bash
echo Received: $1
echo Received: $2
echo Received: $3
echo Received: $4
$ ./no_quotes.sh first second
Received: first
Received: second
Received:
Received:
$ ./no_quotes.sh "one quoted arg"
Received: one
Received: quoted
Received: arg
Received:
$ ./quotes.sh first second
Received: first
Received: second
Received:
Received:
$ ./quotes.sh "one quoted arg"
Received: one quoted arg
Received:
Received:
Received:

For bash and other Bourne-like shells:
bar "$#"

Use "$#" (works for all POSIX compatibles).
[...] , bash features the "$#" variable, which expands to all command-line parameters separated by spaces.
From Bash by example.

I realize this has been well answered but here's a comparison between "$#" $# "$*" and $*
Contents of test script:
# cat ./test.sh
#!/usr/bin/env bash
echo "================================="
echo "Quoted DOLLAR-AT"
for ARG in "$#"; do
echo $ARG
done
echo "================================="
echo "NOT Quoted DOLLAR-AT"
for ARG in $#; do
echo $ARG
done
echo "================================="
echo "Quoted DOLLAR-STAR"
for ARG in "$*"; do
echo $ARG
done
echo "================================="
echo "NOT Quoted DOLLAR-STAR"
for ARG in $*; do
echo $ARG
done
echo "================================="
Now, run the test script with various arguments:
# ./test.sh "arg with space one" "arg2" arg3
=================================
Quoted DOLLAR-AT
arg with space one
arg2
arg3
=================================
NOT Quoted DOLLAR-AT
arg
with
space
one
arg2
arg3
=================================
Quoted DOLLAR-STAR
arg with space one arg2 arg3
=================================
NOT Quoted DOLLAR-STAR
arg
with
space
one
arg2
arg3
=================================

A lot answers here recommends $# or $* with and without quotes, however none seems to explain what these really do and why you should that way. So let me steal this excellent summary from this answer:
+--------+---------------------------+
| Syntax | Effective result |
+--------+---------------------------+
| $* | $1 $2 $3 ... ${N} |
+--------+---------------------------+
| $# | $1 $2 $3 ... ${N} |
+--------+---------------------------+
| "$*" | "$1c$2c$3c...c${N}" |
+--------+---------------------------+
| "$#" | "$1" "$2" "$3" ... "${N}" |
+--------+---------------------------+
Notice that quotes makes all the difference and without them both have identical behavior.
For my purpose, I needed to pass parameters from one script to another as-is and for that the best option is:
# file: parent.sh
# we have some params passed to parent.sh
# which we will like to pass on to child.sh as-is
./child.sh $*
Notice no quotes and $# should work as well in above situation.

#!/usr/bin/env bash
while [ "$1" != "" ]; do
echo "Received: ${1}" && shift;
done;
Just thought this may be a bit more useful when trying to test how args come into your script

If you include $# in a quoted string with other characters the behavior is very odd when there are multiple arguments, only the first argument is included inside the quotes.
Example:
#!/bin/bash
set -x
bash -c "true foo $#"
Yields:
$ bash test.sh bar baz
+ bash -c 'true foo bar' baz
But assigning to a different variable first:
#!/bin/bash
set -x
args="$#"
bash -c "true foo $args"
Yields:
$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'

My SUN Unix has a lot of limitations, even "$#" was not interpreted as desired. My workaround is ${#}. For example,
#!/bin/ksh
find ./ -type f | xargs grep "${#}"
By the way, I had to have this particular script because my Unix also does not support grep -r

Sometimes you want to pass all your arguments, but preceded by a flag (e.g. --flag)
$ bar --flag "$1" --flag "$2" --flag "$3"
You can do this in the following way:
$ bar $(printf -- ' --flag "%s"' "$#")
note: to avoid extra field splitting, you must quote %s and $#, and to avoid having a single string, you cannot quote the subshell of printf.

bar "$#" will be equivalent to bar "$1" "$2" "$3" "$4"
Notice that the quotation marks are important!
"$#", $#, "$*" or $* will each behave slightly different regarding escaping and concatenation as described in this stackoverflow answer.
One closely related use case is passing all given arguments inside an argument like this:
bash -c "bar \"$1\" \"$2\" \"$3\" \"$4\"".
I use a variation of #kvantour's answer to achieve this:
bash -c "bar $(printf -- '"%s" ' "$#")"

Works fine, except if you have spaces or escaped characters. I don't find the way to capture arguments in this case and send to a ssh inside of script.
This could be useful but is so ugly
_command_opts=$( echo "$#" | awk -F\- 'BEGIN { OFS=" -" } { for (i=2;i<=NF;i++) { gsub(/^[a-z] /,"&#",$i) ; gsub(/ $/,"",$i );gsub (/$/,"#",$i) }; print $0 }' | tr '#' \' )

"${array[#]}" is the right way for passing any array in bash. I want to provide a full cheat sheet: how to prepare arguments, bypass and process them.
pre.sh -> foo.sh -> bar.sh.
#!/bin/bash
args=("--a=b c" "--e=f g")
args+=("--q=w e" "--a=s \"'d'\"")
./foo.sh "${args[#]}"
#!/bin/bash
./bar.sh "$#"
#!/bin/bash
echo $1
echo $2
echo $3
echo $4
result:
--a=b c
--e=f g
--q=w e
--a=s "'d'"

Related

Passing arguments between a script and a binary [duplicate]

I am writing a very simple script that calls another script, and I need to propagate the parameters from my current script to the script I am executing.
For instance, my script name is foo.sh and calls bar.sh.
foo.sh:
bar $1 $2 $3 $4
How can I do this without explicitly specifying each parameter?
Use "$#" instead of plain $# if you actually wish your parameters to be passed the same.
Observe:
$ cat no_quotes.sh
#!/bin/bash
echo_args.sh $#
$ cat quotes.sh
#!/bin/bash
echo_args.sh "$#"
$ cat echo_args.sh
#!/bin/bash
echo Received: $1
echo Received: $2
echo Received: $3
echo Received: $4
$ ./no_quotes.sh first second
Received: first
Received: second
Received:
Received:
$ ./no_quotes.sh "one quoted arg"
Received: one
Received: quoted
Received: arg
Received:
$ ./quotes.sh first second
Received: first
Received: second
Received:
Received:
$ ./quotes.sh "one quoted arg"
Received: one quoted arg
Received:
Received:
Received:
For bash and other Bourne-like shells:
bar "$#"
Use "$#" (works for all POSIX compatibles).
[...] , bash features the "$#" variable, which expands to all command-line parameters separated by spaces.
From Bash by example.
I realize this has been well answered but here's a comparison between "$#" $# "$*" and $*
Contents of test script:
# cat ./test.sh
#!/usr/bin/env bash
echo "================================="
echo "Quoted DOLLAR-AT"
for ARG in "$#"; do
echo $ARG
done
echo "================================="
echo "NOT Quoted DOLLAR-AT"
for ARG in $#; do
echo $ARG
done
echo "================================="
echo "Quoted DOLLAR-STAR"
for ARG in "$*"; do
echo $ARG
done
echo "================================="
echo "NOT Quoted DOLLAR-STAR"
for ARG in $*; do
echo $ARG
done
echo "================================="
Now, run the test script with various arguments:
# ./test.sh "arg with space one" "arg2" arg3
=================================
Quoted DOLLAR-AT
arg with space one
arg2
arg3
=================================
NOT Quoted DOLLAR-AT
arg
with
space
one
arg2
arg3
=================================
Quoted DOLLAR-STAR
arg with space one arg2 arg3
=================================
NOT Quoted DOLLAR-STAR
arg
with
space
one
arg2
arg3
=================================
A lot answers here recommends $# or $* with and without quotes, however none seems to explain what these really do and why you should that way. So let me steal this excellent summary from this answer:
+--------+---------------------------+
| Syntax | Effective result |
+--------+---------------------------+
| $* | $1 $2 $3 ... ${N} |
+--------+---------------------------+
| $# | $1 $2 $3 ... ${N} |
+--------+---------------------------+
| "$*" | "$1c$2c$3c...c${N}" |
+--------+---------------------------+
| "$#" | "$1" "$2" "$3" ... "${N}" |
+--------+---------------------------+
Notice that quotes makes all the difference and without them both have identical behavior.
For my purpose, I needed to pass parameters from one script to another as-is and for that the best option is:
# file: parent.sh
# we have some params passed to parent.sh
# which we will like to pass on to child.sh as-is
./child.sh $*
Notice no quotes and $# should work as well in above situation.
#!/usr/bin/env bash
while [ "$1" != "" ]; do
echo "Received: ${1}" && shift;
done;
Just thought this may be a bit more useful when trying to test how args come into your script
If you include $# in a quoted string with other characters the behavior is very odd when there are multiple arguments, only the first argument is included inside the quotes.
Example:
#!/bin/bash
set -x
bash -c "true foo $#"
Yields:
$ bash test.sh bar baz
+ bash -c 'true foo bar' baz
But assigning to a different variable first:
#!/bin/bash
set -x
args="$#"
bash -c "true foo $args"
Yields:
$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'
My SUN Unix has a lot of limitations, even "$#" was not interpreted as desired. My workaround is ${#}. For example,
#!/bin/ksh
find ./ -type f | xargs grep "${#}"
By the way, I had to have this particular script because my Unix also does not support grep -r
Sometimes you want to pass all your arguments, but preceded by a flag (e.g. --flag)
$ bar --flag "$1" --flag "$2" --flag "$3"
You can do this in the following way:
$ bar $(printf -- ' --flag "%s"' "$#")
note: to avoid extra field splitting, you must quote %s and $#, and to avoid having a single string, you cannot quote the subshell of printf.
bar "$#" will be equivalent to bar "$1" "$2" "$3" "$4"
Notice that the quotation marks are important!
"$#", $#, "$*" or $* will each behave slightly different regarding escaping and concatenation as described in this stackoverflow answer.
One closely related use case is passing all given arguments inside an argument like this:
bash -c "bar \"$1\" \"$2\" \"$3\" \"$4\"".
I use a variation of #kvantour's answer to achieve this:
bash -c "bar $(printf -- '"%s" ' "$#")"
Works fine, except if you have spaces or escaped characters. I don't find the way to capture arguments in this case and send to a ssh inside of script.
This could be useful but is so ugly
_command_opts=$( echo "$#" | awk -F\- 'BEGIN { OFS=" -" } { for (i=2;i<=NF;i++) { gsub(/^[a-z] /,"&#",$i) ; gsub(/ $/,"",$i );gsub (/$/,"#",$i) }; print $0 }' | tr '#' \' )
"${array[#]}" is the right way for passing any array in bash. I want to provide a full cheat sheet: how to prepare arguments, bypass and process them.
pre.sh -> foo.sh -> bar.sh.
#!/bin/bash
args=("--a=b c" "--e=f g")
args+=("--q=w e" "--a=s \"'d'\"")
./foo.sh "${args[#]}"
#!/bin/bash
./bar.sh "$#"
#!/bin/bash
echo $1
echo $2
echo $3
echo $4
result:
--a=b c
--e=f g
--q=w e
--a=s "'d'"

Take file as an input and run awk command on it [duplicate]

I am writing a very simple script that calls another script, and I need to propagate the parameters from my current script to the script I am executing.
For instance, my script name is foo.sh and calls bar.sh.
foo.sh:
bar $1 $2 $3 $4
How can I do this without explicitly specifying each parameter?
Use "$#" instead of plain $# if you actually wish your parameters to be passed the same.
Observe:
$ cat no_quotes.sh
#!/bin/bash
echo_args.sh $#
$ cat quotes.sh
#!/bin/bash
echo_args.sh "$#"
$ cat echo_args.sh
#!/bin/bash
echo Received: $1
echo Received: $2
echo Received: $3
echo Received: $4
$ ./no_quotes.sh first second
Received: first
Received: second
Received:
Received:
$ ./no_quotes.sh "one quoted arg"
Received: one
Received: quoted
Received: arg
Received:
$ ./quotes.sh first second
Received: first
Received: second
Received:
Received:
$ ./quotes.sh "one quoted arg"
Received: one quoted arg
Received:
Received:
Received:
For bash and other Bourne-like shells:
bar "$#"
Use "$#" (works for all POSIX compatibles).
[...] , bash features the "$#" variable, which expands to all command-line parameters separated by spaces.
From Bash by example.
I realize this has been well answered but here's a comparison between "$#" $# "$*" and $*
Contents of test script:
# cat ./test.sh
#!/usr/bin/env bash
echo "================================="
echo "Quoted DOLLAR-AT"
for ARG in "$#"; do
echo $ARG
done
echo "================================="
echo "NOT Quoted DOLLAR-AT"
for ARG in $#; do
echo $ARG
done
echo "================================="
echo "Quoted DOLLAR-STAR"
for ARG in "$*"; do
echo $ARG
done
echo "================================="
echo "NOT Quoted DOLLAR-STAR"
for ARG in $*; do
echo $ARG
done
echo "================================="
Now, run the test script with various arguments:
# ./test.sh "arg with space one" "arg2" arg3
=================================
Quoted DOLLAR-AT
arg with space one
arg2
arg3
=================================
NOT Quoted DOLLAR-AT
arg
with
space
one
arg2
arg3
=================================
Quoted DOLLAR-STAR
arg with space one arg2 arg3
=================================
NOT Quoted DOLLAR-STAR
arg
with
space
one
arg2
arg3
=================================
A lot answers here recommends $# or $* with and without quotes, however none seems to explain what these really do and why you should that way. So let me steal this excellent summary from this answer:
+--------+---------------------------+
| Syntax | Effective result |
+--------+---------------------------+
| $* | $1 $2 $3 ... ${N} |
+--------+---------------------------+
| $# | $1 $2 $3 ... ${N} |
+--------+---------------------------+
| "$*" | "$1c$2c$3c...c${N}" |
+--------+---------------------------+
| "$#" | "$1" "$2" "$3" ... "${N}" |
+--------+---------------------------+
Notice that quotes makes all the difference and without them both have identical behavior.
For my purpose, I needed to pass parameters from one script to another as-is and for that the best option is:
# file: parent.sh
# we have some params passed to parent.sh
# which we will like to pass on to child.sh as-is
./child.sh $*
Notice no quotes and $# should work as well in above situation.
#!/usr/bin/env bash
while [ "$1" != "" ]; do
echo "Received: ${1}" && shift;
done;
Just thought this may be a bit more useful when trying to test how args come into your script
If you include $# in a quoted string with other characters the behavior is very odd when there are multiple arguments, only the first argument is included inside the quotes.
Example:
#!/bin/bash
set -x
bash -c "true foo $#"
Yields:
$ bash test.sh bar baz
+ bash -c 'true foo bar' baz
But assigning to a different variable first:
#!/bin/bash
set -x
args="$#"
bash -c "true foo $args"
Yields:
$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'
My SUN Unix has a lot of limitations, even "$#" was not interpreted as desired. My workaround is ${#}. For example,
#!/bin/ksh
find ./ -type f | xargs grep "${#}"
By the way, I had to have this particular script because my Unix also does not support grep -r
Sometimes you want to pass all your arguments, but preceded by a flag (e.g. --flag)
$ bar --flag "$1" --flag "$2" --flag "$3"
You can do this in the following way:
$ bar $(printf -- ' --flag "%s"' "$#")
note: to avoid extra field splitting, you must quote %s and $#, and to avoid having a single string, you cannot quote the subshell of printf.
bar "$#" will be equivalent to bar "$1" "$2" "$3" "$4"
Notice that the quotation marks are important!
"$#", $#, "$*" or $* will each behave slightly different regarding escaping and concatenation as described in this stackoverflow answer.
One closely related use case is passing all given arguments inside an argument like this:
bash -c "bar \"$1\" \"$2\" \"$3\" \"$4\"".
I use a variation of #kvantour's answer to achieve this:
bash -c "bar $(printf -- '"%s" ' "$#")"
Works fine, except if you have spaces or escaped characters. I don't find the way to capture arguments in this case and send to a ssh inside of script.
This could be useful but is so ugly
_command_opts=$( echo "$#" | awk -F\- 'BEGIN { OFS=" -" } { for (i=2;i<=NF;i++) { gsub(/^[a-z] /,"&#",$i) ; gsub(/ $/,"",$i );gsub (/$/,"#",$i) }; print $0 }' | tr '#' \' )
"${array[#]}" is the right way for passing any array in bash. I want to provide a full cheat sheet: how to prepare arguments, bypass and process them.
pre.sh -> foo.sh -> bar.sh.
#!/bin/bash
args=("--a=b c" "--e=f g")
args+=("--q=w e" "--a=s \"'d'\"")
./foo.sh "${args[#]}"
#!/bin/bash
./bar.sh "$#"
#!/bin/bash
echo $1
echo $2
echo $3
echo $4
result:
--a=b c
--e=f g
--q=w e
--a=s "'d'"

Bash - How to force literal in subshell string?

I'd like to control variable expansion when executing a shell command using sudo bash -c.
I know I can do it from a normal shell:
bash$ export FOO=foo
bash$ export BAR=bar
bash$ echo "expand $FOO but not "'$BAR'""
expand foo but not $BAR
How can I do the above using sudo bash -c?
bash$ sudo bash -c "echo "expand $FOO but not "'$BAR'"""
expand
bash$ sudo bash -c 'echo "expand $FOO but not "'$BAR'""'
expand but not bar
You can use this with escaped $ that you don't want to expand:
$> bash -c "echo \"expand $FOO but not \"'\$BAR'"
expand foo but not $BAR
However I recommend using here-doc to avoid escaping:
# original echo replaced with printf
$> printf 'expand %s but not %s\n' "$FOO" '$BAR'
expand foo but not $BAR
# prints in here-doc with bash
$> bash<<-'EOF'
printf 'expand %s but not %s\n' "$FOO" '$BAR'
EOF
expand foo but not $BAR
Pass arguments rather than trying to generate a string to pass to bash.
$ bash -c 'echo "expand $1 but not $2"' _ "$FOO" '$BAR'
expand 5 but not $BAR
(The _ is just a dummy value to set $0 in the script specified by -c.)

Iterate over arguments in a bash script and make use of their numbers

If I want to iterate over all arguments it is as easy as for i in "$#"; do .... However, let's say I want to start with the second argument and also make use of the arguments' positions for some basic calculation.
As an example I want to shorten these commands into one loop:
grep -v 'foobar' "$2" | grep -f $file > output1.txt
grep -v 'foobar' "$3" | grep -f $file > output2.txt
grep -v 'foobar' "$4" | grep -f $file > output3.txt
grep -v 'foobar' "$5" | grep -f $file > output4.txt
I tried many variations like for i in {2..5}; do grep -v 'foobar' "$$i" | grep -f $file > output$(($i-1)).txt; done; however, it seems bash expansion doesn't work like this.
EDIT:
Seems I made a mistake not emphasizing that I need to make use of the argument's position/number (i.e., 2 from $2). It's important because the output files get used separately later in the script. All of the provided answers so far seem correct but I don't know how to use them to make use of the argument's "number".
Couple correct answers already, another way could be:
for (( i=2; i <= "$#"; i++ )); do
echo "arg position: ${i}"
echo "arg value: ${!i}"
done
If you do not want to shift off the first unneeded arguments you can use the indirection expansion:
for i in {2..5}; do
echo "${!i}"
done
You can also make use of array indexes directly:
#!/bin/bash
for i in "${#:2}"; do
echo "$i"
done
Which will iterate over the arguments beginning with the second argument. It will also preserve whitespace in arguments when quoted. e.g.
$ bash args.sh one two "three four" five
two
three four
five
You just need to use shift to move positional parameter once (thus discarding $1):
fn() { arg1="$1"; shift; for arg; do echo "$arg"; done; }
Call it as:
fn val1 val2 val3 val4
Output:
val2
val3
val4
Above answers are correct. Another approach:
a=("${#:2}")
for i in ${!a[#]}; do
echo "$i = ${a[i]}"
done
$# is similar to an array, with the small difference that it is one-based and not zero-based ($0 is the name of the shell or shell script), and it can't be indexed (${#[1]} won't work to access $1). You can use parameter expansion (more specifically: substring expansion) to access a subset of your positional parameters:
$ set arg1 arg2 arg3 arg4 arg5 # Set $1, $2, $3, $4, $5
$ for i in "${#:2}"; do echo "$i"; done
arg2
arg3
arg4
arg5
Notice that this will not actually allow you to use the index of the argument and the argument itself. To do that, you'd have to either turn the positional parameters into a regular array and deal with the index offset by one ($1 would have index 0), or use indirect expansion (see andlrc's answer).
Example of the first method:
$ set arg1 arg2 arg3 arg4 arg5
$ args=("$#")
$ for i in 3 2 5 1 4; do echo "\$$i is ${args[$((i-1))]}"; done
$3 is arg3
$2 is arg2
$5 is arg5
$1 is arg1
$4 is arg4
Clearly, ${args[$((i-1))]} is rather messy, but it works.
Side note: if you want to access all positional parameters, you can use a convenient shorthand:
$ for i; do echo "$i"; done
arg1
arg2
arg3
arg4
arg5
for without the in part loops over all positional parameters (see manual).

Pass ALL Arguments from Bash Script to Another Command [duplicate]

I am writing a very simple script that calls another script, and I need to propagate the parameters from my current script to the script I am executing.
For instance, my script name is foo.sh and calls bar.sh.
foo.sh:
bar $1 $2 $3 $4
How can I do this without explicitly specifying each parameter?
Use "$#" instead of plain $# if you actually wish your parameters to be passed the same.
Observe:
$ cat no_quotes.sh
#!/bin/bash
echo_args.sh $#
$ cat quotes.sh
#!/bin/bash
echo_args.sh "$#"
$ cat echo_args.sh
#!/bin/bash
echo Received: $1
echo Received: $2
echo Received: $3
echo Received: $4
$ ./no_quotes.sh first second
Received: first
Received: second
Received:
Received:
$ ./no_quotes.sh "one quoted arg"
Received: one
Received: quoted
Received: arg
Received:
$ ./quotes.sh first second
Received: first
Received: second
Received:
Received:
$ ./quotes.sh "one quoted arg"
Received: one quoted arg
Received:
Received:
Received:
For bash and other Bourne-like shells:
bar "$#"
Use "$#" (works for all POSIX compatibles).
[...] , bash features the "$#" variable, which expands to all command-line parameters separated by spaces.
From Bash by example.
I realize this has been well answered but here's a comparison between "$#" $# "$*" and $*
Contents of test script:
# cat ./test.sh
#!/usr/bin/env bash
echo "================================="
echo "Quoted DOLLAR-AT"
for ARG in "$#"; do
echo $ARG
done
echo "================================="
echo "NOT Quoted DOLLAR-AT"
for ARG in $#; do
echo $ARG
done
echo "================================="
echo "Quoted DOLLAR-STAR"
for ARG in "$*"; do
echo $ARG
done
echo "================================="
echo "NOT Quoted DOLLAR-STAR"
for ARG in $*; do
echo $ARG
done
echo "================================="
Now, run the test script with various arguments:
# ./test.sh "arg with space one" "arg2" arg3
=================================
Quoted DOLLAR-AT
arg with space one
arg2
arg3
=================================
NOT Quoted DOLLAR-AT
arg
with
space
one
arg2
arg3
=================================
Quoted DOLLAR-STAR
arg with space one arg2 arg3
=================================
NOT Quoted DOLLAR-STAR
arg
with
space
one
arg2
arg3
=================================
A lot answers here recommends $# or $* with and without quotes, however none seems to explain what these really do and why you should that way. So let me steal this excellent summary from this answer:
+--------+---------------------------+
| Syntax | Effective result |
+--------+---------------------------+
| $* | $1 $2 $3 ... ${N} |
+--------+---------------------------+
| $# | $1 $2 $3 ... ${N} |
+--------+---------------------------+
| "$*" | "$1c$2c$3c...c${N}" |
+--------+---------------------------+
| "$#" | "$1" "$2" "$3" ... "${N}" |
+--------+---------------------------+
Notice that quotes makes all the difference and without them both have identical behavior.
For my purpose, I needed to pass parameters from one script to another as-is and for that the best option is:
# file: parent.sh
# we have some params passed to parent.sh
# which we will like to pass on to child.sh as-is
./child.sh $*
Notice no quotes and $# should work as well in above situation.
#!/usr/bin/env bash
while [ "$1" != "" ]; do
echo "Received: ${1}" && shift;
done;
Just thought this may be a bit more useful when trying to test how args come into your script
If you include $# in a quoted string with other characters the behavior is very odd when there are multiple arguments, only the first argument is included inside the quotes.
Example:
#!/bin/bash
set -x
bash -c "true foo $#"
Yields:
$ bash test.sh bar baz
+ bash -c 'true foo bar' baz
But assigning to a different variable first:
#!/bin/bash
set -x
args="$#"
bash -c "true foo $args"
Yields:
$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'
My SUN Unix has a lot of limitations, even "$#" was not interpreted as desired. My workaround is ${#}. For example,
#!/bin/ksh
find ./ -type f | xargs grep "${#}"
By the way, I had to have this particular script because my Unix also does not support grep -r
Sometimes you want to pass all your arguments, but preceded by a flag (e.g. --flag)
$ bar --flag "$1" --flag "$2" --flag "$3"
You can do this in the following way:
$ bar $(printf -- ' --flag "%s"' "$#")
note: to avoid extra field splitting, you must quote %s and $#, and to avoid having a single string, you cannot quote the subshell of printf.
bar "$#" will be equivalent to bar "$1" "$2" "$3" "$4"
Notice that the quotation marks are important!
"$#", $#, "$*" or $* will each behave slightly different regarding escaping and concatenation as described in this stackoverflow answer.
One closely related use case is passing all given arguments inside an argument like this:
bash -c "bar \"$1\" \"$2\" \"$3\" \"$4\"".
I use a variation of #kvantour's answer to achieve this:
bash -c "bar $(printf -- '"%s" ' "$#")"
Works fine, except if you have spaces or escaped characters. I don't find the way to capture arguments in this case and send to a ssh inside of script.
This could be useful but is so ugly
_command_opts=$( echo "$#" | awk -F\- 'BEGIN { OFS=" -" } { for (i=2;i<=NF;i++) { gsub(/^[a-z] /,"&#",$i) ; gsub(/ $/,"",$i );gsub (/$/,"#",$i) }; print $0 }' | tr '#' \' )
"${array[#]}" is the right way for passing any array in bash. I want to provide a full cheat sheet: how to prepare arguments, bypass and process them.
pre.sh -> foo.sh -> bar.sh.
#!/bin/bash
args=("--a=b c" "--e=f g")
args+=("--q=w e" "--a=s \"'d'\"")
./foo.sh "${args[#]}"
#!/bin/bash
./bar.sh "$#"
#!/bin/bash
echo $1
echo $2
echo $3
echo $4
result:
--a=b c
--e=f g
--q=w e
--a=s "'d'"

Resources