What does ${1} and ${*} do? [duplicate] - linux

This question already has answers here:
What are the special dollar sign shell variables?
(4 answers)
Closed 3 years ago.
This Nagios script uses ${1} and ${*} like so
if [ "${1}" ]; then
if [ "${ERRORSTRING}" ]; then
echo "${ERRORSTRING} ${OKSTRING}" | sed s/"^\/ "// | mail -s "$(hostname -s): ${0} reports errors\
" -E ${*}
fi
else
if [ "${ERRORSTRING}" -o "${OKSTRING}" ]; then
echo "${ERRORSTRING} ${OKSTRING}" | sed s/"^\/ "//
exit ${ERR}
else
echo no zpool volumes found
exit 3
fi
fi
Question
What does ${1} and ${*} do?

The command-line arguments $1, $2, $3,...$9 are positional parameters, with $0 pointing to the actual command, program, shell script, or function and $1, $2, $3, ...$9 as the arguments to the command.
"$*" special parameter takes the entire list as one argument with spaces between and the "$#" special parameter takes the entire list and separates it into separate arguments.
Suppose test.sh given below:
#!/bin/sh
echo "File Name: $0"
echo "First Parameter : $1"
echo "First Parameter : $2"
echo "Quoted Values: $#"
echo "Quoted Values: $*"
echo "Total Number of Parameters : $#"

Quoting from Special Parameters in the manual:
*
Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, it expands to a single word
with the value of each parameter separated by the first character of
the IFS special variable. That is, "$*" is equivalent to "$1c$2c…",
where c is the first character of the value of the IFS variable. If
IFS is unset, the parameters are separated by spaces. If IFS is null,
the parameters are joined without intervening separators.
$1 refers to a Positional Parameter.
Assume the following script:
echo "${1}"
echo "${*}"
Upon invoking by saying:
bash scriptname foo bar baz
it would produce:
foo
foo bar baz
Now observe the effect of the variable IFS. Given the script:
IFS=
echo "${1}"
echo "${*}"
echo "${#}"
Invoking it by saying:
bash scriptname "foo:hey you" bar baz
would produce:
foo:hey you
foo:hey youbarbaz
foo:hey you bar baz

$1 is used(to display or to get input for user interactively) to display First parameter.
$* is used to show all parameters entered.
# cat schecking.sh
#!/bin/bash
echo "All values:" "$*"
echo "Total number of Parameter(s) :" "$#"
# ./schecking.sh
All values:
Total number of Parameter(s) : 0
# ./schecking.sh It will help us to check
All values: It will help us to check
Total number of Parameter(s) : 6
# cat schecking.sh
#!/bin/bash
echo "First value is:" $1
echo "All values:" "$*"
echo "Total number of Parameter(s) :" "$#"
# ./schecking.sh It will help us to check
First value is: It
All values: It will help us to check
Total number of Parameter(s) : 6

Related

how to pass list of values to shell argument in script

Script should pass list of values to the argument and should validate if there is one argument is passed and no list.
for example
./script --arg hi
script should do the --arg command and add/delete hi
./script --arg "hi how are you "
in this case no of arguments passed to arg how to give exception or through error if user enter above values to arg1.
function test() {
filename=$1
echo $filename
case "$2" in
a)
echo $3 >> $filename
echo "add "
# cat $filename
shift
shift
;;
exit
}
test $fileName $3 $4
argsCount here will do the trick if you want to have a check on the number of the arguments passed. In the below example, I am passing 3 arguments and validation whether the arguments counts is equal to 3, if not it will exit from the script.
#!/usr/bin/env bash
set -ex
set -o pipefail
copyConfigFrom=$1
hostConfigFileName=$2
hostnameEmail=$3
argsCount="$#"
if [ "$argsCount" -ne 3 ]; then
echo "Usage: $0 copyConfigFrom hostConfigFileName hostnameEmail"
exit 1
fi

bash separate parameters with specific delimiter

I am searching for a command, that separates all given parameters with a specific delimiter, and outputs them quoted.
Example (delimiter is set to be a colon :):
somecommand "this is" "a" test
should output
"this is":"a":"test"
I'm aware that the shell interprets the "" quotes before passing the parameters to the command. So what the command should actually do is to print out every given parameter in quotes and separate all these with a colon.
I'm also not seeking for a bash-only solution, but for the most elegant solution.
It is very easy to just loop over an array of these elements and do that, but the problem is that I have to use this inside a gnu makefile which only allows single line shell commands and uses sh instead of bash.
So the simpler the better.
How about
somecommand () {
printf '"%s"\n' "$#" | paste -s -d :
}
Use printf to add the quotes and print every entry on a separate line, then use paste with the -s ("serial") option and a colon as the delimiter.
Can be called like this:
$ somecommand "this is" "a" test
"this is":"a":"test"
apply_delimiter () {
(( $# )) || return
local res
printf -v res '"%s":' "$#"
printf '%s\n' "${res%:}"
}
Usage example:
$ apply_delimiter hello world "how are you"
"hello":"world":"how are you"
As indicated in a number of the comments, a simple "loop-over" approach, looping over each of the strings passed as arguments is a fairly straight-forward way to approach it:
delimit_colon() {
local first=1
for i in "$#"; do
if [ "$first" -eq 1 ]; then
printf "%s" "$i"
first=0
else
printf ":%s" "$i"
fi
done
printf "\n"
}
Which when combined with a short test script could be:
#!/bin/bash
delimit_colon() {
local first=1
for i in "$#"; do
if [ "$first" -eq 1 ]; then
printf "%s" "$i"
first=0
else
printf ":%s" "$i"
fi
done
printf "\n"
}
[ -z "$1" ] && { ## validate input
printf "error: insufficient input\n"
exit 1
}
delimit_colon "$#"
exit 0
Test Input/Output
$ bash delimitargs.sh "this is" "a" test
this is:a:test
Here a solution using the z-shell:
#!/usr/bin/zsh
# this is "somecommand"
echo '"'${(j_":"_)#}'"'
If you have them in an array already, you can use this command
MYARRAY=("this is" "a" "test")
joined_string=$(IFS=:; echo "$(MYARRAY[*])")
echo $joined_string
Setting the IFS (internal field separator) will be the character separator. Using echo on the array will display the array using the newly set IFS. Putting those commands in $() will put the output of the echo into joined_string.

How to check if there is a parameter provided in bash?

I wanted to see if there is a parameter provided. But i don't know why this code wont work. What am i doing wrong?
if ![ -z "$1" ]
then
echo "$1"
fi
Let's ask shellcheck:
In file line 1:
if ![ -z "$1" ]
^-- SC1035: You need a space here.
In other words:
if ! [ -z "$1" ]
then
echo "$1"
fi
If, as per your comment, it still doesn't work and you happen to be doing this from a function, the function's parameters will mask the script's parameters, and we have to pass them explicitly:
In file line 8:
call_it
^-- SC2119: Use call_it "$#" if function's $1 should mean script's $1.
You could check the number of given parameters. $# represents the number of parameters - or the length of the array containing the parameters - passed to a script or a function. If it is zero then no parameters have been passed. Here is a good read about positional parameters in general.
$ cat script
#!/usr/bin/env bash
if (( $# == 0 )); then
echo "No parameters provided"
else
echo "Number of parameters is $#"
fi
Result:
$ ./script
No parameters provided
$ ./script first
Number of parameters is 1
$ ./script first second
Number of parameters is 2
If you would like to check the parameters passed to the script with a function then you would have to provide the script parameters to the function:
$ cat script
#!/usr/bin/env bash
_checkParameters()
{
if (( $1 == 0 )); then
echo "No parameters provided"
else
echo "Number of parameters is $1"
fi
}
_checkParameters $#
This will return the same results as in the first example.
Also related: What are the special dollar sign shell variables?

How to catch missing argument in function mistake?

I have just made the following mistake, where I am passing an argument to a function which is empty.
var1="ok"
var2=$notDefined
func $var1 $var2
func() {
var1=$1
var2=$2
echo $var1
echo $var2
}
For each argument in the function I could do
if [ -z $1 ]; then echo "Empty argument"; fi
But is there a more generic method to do this, so it is easy reusable, and would perhaps even tell the variable name that is empty?
You can stop whole script by set -u. It will fail if you try to use unset variable. It is very general approach.
Bash will output following localized message to standard error:
bash: x: unbound variable
You want to use the ? bash variable substitution operator:
var1=${1:?"undefined!"}
If $1 exists and isn't null, var1 is set to its value, otherwise bash prints 1 followed by "undefined!" and aborts the current command or script. This syntax can used for any bash variable.
In your case the empty variables are created, because there are too few arguments to the function.
You can get the number of passed arguments via $#. All variables that use $n with a higher number n must then be empty. You could check for a sufficiently high number of arguments at the beginning of your function.
#!/bin/bash
var1="ok"
var2=$notDefined
func() {
if [[ $# -ge 2 ]]; then
var1=$1
var2=$2
echo $var1
echo $var2
else
echo "Missing values"
fi
}
func $var1 $var2
Here it is running
./test.sh
Missing values
Here it is with two values:
#!/bin/bash
var1="ok"
var2="dokie"
func() {
if [[ $# -ge 2 ]]; then
var1=$1
var2=$2
echo $var1
echo $var2
else
echo "Missing values"
fi
}
func $var1 $var2
Results with:
./test.sh
ok
dokie

Multiple variables in loop input?

When using the following:
for what in $#; do
read -p "Where?" where
grep -H "$what" $where -R | cut -d: -f1
How can I, instead of using read to define a user-variable, have a second variable input along with the first variable when calling the script.
For example, the ideal usage I believe I can get is something like:
sh scriptname var1 var2
But my understanding is that the for... line is for looping the subsequent entires into the one variable; what would I need to change to input multiple variables?
As an aside: using | cut -D: -f1 is not safe, because grep does not escape colons in filenames. To see what I mean, you can try this:
ghoti#pc:~$ echo bar:baz > foo
ghoti#pc:~$ echo baz > foo:bar
ghoti#pc:~$ grep -Hr ba .
./foo:bar:baz
./foo:bar:baz
Clarity .. there is not.
So ... let's clarify what you're looking for.
Do you want to search for one string in multiple files? Or,
Do you want to search for multiple strings in one file?
If the former, then the following might work:
#!/bin/bash
if [[ "$#" -lt 2 ]]; then
echo "Usage: `basename $0` string file [file ...]
exit 1
fi
what="$1"
shift # discard $1, move $2 to $1, $3 to $2, etc.
for where in "$#"; do
grep -HlR "$what" "$where" -R
done
And if the latter, then this would be the way:
#!/bin/bash
if [[ "$#" -lt 2 ]]; then
echo "Usage: `basename $0` file string [string ...]
exit 1
fi
where="$1"
shift
for what in "$#"; do
grep -lR "$what" "$where"
done
Of course, this one might be streamlined if you concatenated your strings with an or bar, then used egrep. Depends on what you're actually looking for.
You can get parameters passed on the command line with $1 $2 etc.
Read up on positional parameters: http://www.linuxcommand.org/wss0130.php. You don't need a for loop to parse them.
sh scriptname var1 var2
v1=$1 # contains var1
v2=$2 # contains var1
$# is basically just a list of all the positional parameters: $1 $2 $3 etc.

Resources