How do I find the number of arguments passed to a Bash script? - bash

How do I find the number of arguments passed to a Bash script?
This is what I have currently:
#!/bin/bash
i=0
for var in "$#"
do
i=i+1
done
Are there other (better) ways of doing this?

The number of arguments is $#
Search for it on this page to learn more:
http://tldp.org/LDP/abs/html/internalvariables.html#ARGLIST

#!/bin/bash
echo "The number of arguments is: $#"
a=${#}
echo "The total length of all arguments is: ${#a}: "
count=0
for var in "$#"
do
echo "The length of argument '$var' is: ${#var}"
(( count++ ))
(( accum += ${#var} ))
done
echo "The counted number of arguments is: $count"
echo "The accumulated length of all arguments is: $accum"

to add the original reference:
You can get the number of arguments from the special parameter $#. Value of 0 means "no arguments". $# is read-only.
When used in conjunction with shift for argument processing, the special parameter $# is decremented each time Bash Builtin shift is executed.
see Bash Reference Manual in section 3.4.2 Special Parameters:
"The shell treats several parameters specially. These parameters may only be referenced"
and in this section for keyword $# "Expands to the number of positional parameters in decimal."

Below is the easy one -
cat countvariable.sh
echo "$#" | awk '{print NF}'
Output :
#./countvariable.sh 1 2 3 4 5 6
6
#./countvariable.sh 1 2 3 4 5 6 apple orange
8

Related

In bash how to use the last argument- and adding all other arguments to array

I have a script where the user can add as many arguments as he would like (numbers).
The script will sum all the numbers beside the last number - The last number (argument) is the number that I need to divide by
For example:
./test.sh 2 2 6 5
This will sum the first 3 numbers (2+2+6) and divide the answer by 5 (the last argument)
How can I use the last argument? Echo ????
How can I move loop the first arguments besides the last one ā€“ I would like that all 3 arguments will be added to an array and I can loop it
Please note that the number of arguments can be changed
How can I use the last argument? Echo ????
Granting $# > 0, you can use "${!#}".
How can I move loop the first arguments besides the last one ā€“ I would
like that all 3 arguments will be added to an array and I can loop it
Again granting $# > 0, you can refer to "${#:1:$# - 1}".
Read the Arrays section in the bash manual to know how to properly expand arrays.
I also recommend learning how quoting works and knowing the dangers of unwanted word splitting and globbing.
Shortly (with bashisms)
As this question is tagged integer-arithmetic and bash:
Here is a small and efficient script:
#!/bin/bash
addVals=${*: 1 : $# - 1}
declare -i intResult=" ( ${addVals// /+} ) / ${#: -1} "
echo $intResult
But there's no loop...
Long answer
How can I use the last argument? Echo ????
You could make your tries in command line:
set -- 2 2 6 5
Then
echo $#
2 2 6 5
echo ${*: 3}
6 5
echo ${*: -1}
5
echo ${*: 1 : -1}
bash: -1: substring expression < 0
echo $#
4
echo ${*: 1 : $# -1}
2 2 6
Ok, then
someVar=${*: 1 : $# -1}
echo ${someVar// /:SpaceReplacment:}
2:SpaceReplacment:2:SpaceReplacment:6
so
declare -i result
result=" ( ${someVar// /+} ) / ${*: -1} "
echo $result
2
How can I move loop the first arguments besides the last one ā€“ I would like that all 3 arguments will be added to an array and I can loop it
Still forward, under command line...
someArray=("${#: 1: $# -1 }")
Use declare -p to show $someArray's content:
declare -p someArray
declare -a someArray=([0]="2" [1]="2" [2]="6")
Then
declare -i mySum=0
for i in "${someArray[#]}";do
mySum+=i
done
echo $mySum
10
echo $(( mySum / ${*: -1} ))
2
Please note that the number of arguments can be changed
Please note:
Using double quotes allow processing of strings containing spaces:
set -- foo bar 'foo bar baz'
echo ${2}
bar
echo ${*: $# }
foo bar baz
Difference betweeen use of "$#" (array to array) and "$*" (array to string)
set -- foo bar 'foo bar' 'foo bar baz'
If I take 3 first elements:
someArray=("${#: 1: $# -1 }")
declare -p someArray
declare -a someArray=([0]="foo" [1]="bar" [2]="foo bar")
But
someArray=("${*: 1: $# -1 }")
declare -p someArray
declare -a someArray=([0]="foo bar foo bar")
There are about a thousand ways of doing this. As you would like to make use of integer arithmetic, you can do the following in bash
A short semi-cryptic version would be:
IFS=+
echo $(( ( ${*} - ${#:-1} ) / ${#:-1} ))
Here we make use of the difference between "${*}" and "${#}" to perform the sum by setting IFS=+ (See What is the difference between "$#" and "$*" in Bash?)
A long classic approach would be:
for i in "$#"; do ((s+=i)); done
echo $(( (s-${#:-1})/${#:-1} ))
It's easier to sum all terms and subtract the last term afterwards

Check if every argument is an integer in shell

I've been killing myself over this trying to figure it out, and I know it's probably super simple, so hoping a new pair of eyes can help. I have a Bourne shell (sh) script that I'm writing and it takes a list of integers as the input (and amount from 1 integer up, ideally I'd like to take both positive and negative ints). I'm trying to do an error check for the case if someone inputs something other than an integer. They could input "1 2 3 4 5 a" and it'd give an error, because the a is not an int.
I have an error check for no inputs that works, and I have code that does stuff to the list of integers themselves, but even when strings are given it still gets to my final code block.
I currently have a for loop to iterate through each item in the list of integers, then an if loop to give the error message if the argument in question isn't an int. I've tried a few different versions of this, but this is the most recent one so I've put it below.
for i in $#; do
if [ $i -ge 0 ] 2>/dev/null; then
echo "Invalid input: integers only."
exit 1
fi
done
#!/bin/sh
#
for i in "$#"
do
case "${i#[-+]}" in
0)
echo cannot be 0?
exit 1
;;
*[!0-9]* | '')
echo not int
exit 2
;;
esac
done
echo i\'m ok
This should work, for both positive and negative ints. And if you admit that 0 is an integer, just delete the first case.
Almost duplicate: BASH: Test whether string is valid as an integer?
And here is a good answer for posix. https://stackoverflow.com/a/18620446/7714132
You could use a regex:
my_script.sh
for i in $# ; do
if ! [[ "$i" =~ ^-?[0-9]+$ ]] ; then
echo "Invalid input: integers only."
exit 1
fi
done
Example:
$ sh my_script.sh 1 2 3 4
$ sh my_script.sh 1 2 -12
$ sh my_script.sh 1 2 -12-2
Invalid input: integers only.
$ sh my_script.sh 1 2 a b
Invalid input: integers only.
Explanation of the regex:
^: beginning of the string
-?: 0 or 1 times the character -
[0-9]+: 1 or more digit
$: end of the string
In POSIX sh, you can match your string against a glob with case:
#!/bin/sh
for i
do
case "$i" in
*[!0-9]*)
echo "Integers only" >&2
exit 1
esac
done
echo "Ok"
Here's how it runs:
$ ./myscript 1 2 3 4 5
Ok
$ ./myscript 1 2 3 4 5 a
Integers only
The problem with your approach is primarily that you're checking for success rather than failure: [ will fail when the input is invalid.

Command line arguments in shell script

Following is the shell script which iterates over command line arguments and prints the values
for var in "$#"
do
echo $var
done
Now if i want to iterate from the second command line argument (the first argument being used for some other purpose), what is the command to exclude the first argument alone in iteration ?
Use shift:
#!/usr/bin/env bash
shift
for var in "$#"; do
echo "$var"
done
The first argument is in $1, so doing
var1=$1
will store it into var1
You can then use shift to "delete" the first arg and still use your for loop:
~$cat s.sh
var1=$1
shift
echo var1=$var1
for var in "$#"
do
echo $var
done
~$ ./s.sh 1 2 3
var1=1
2
3
From man bash:
shift [n]
The positional parameters from n+1 ... are renamed to $1 .... Parameters
represented by the numbers $# down to $#-n+1 are unset. n
must be a non-negative number less than or equal to $#. If n
is 0, no parameters are changed. If n is not given, it is assumed to be 1. If n is greater than $#, the positional parameters
are not changed. The return status is greater than zero if n is
greater than $# or less than zero; otherwise 0.
This example behaves more like a standard unix command line utility. Options are processed in any order and can have modifiers. It does not work for every situation (like modifiers with spaces in them), but I've found it to be very useful for scripts that I want to have some default behavior, but occasionally I want to modify one or more parameters.
#!/bin/bash
#defaults
VAR1="Yours"
VAR2="Mine"
VAR3="Ours"
SHARENICE="false"
while [ $# -gt 0 ]; do
case "$1" in
#single parameter
-s) SHARENICE="true"
shift
;;
#modified parameters
-1) VAR1="$2"
shift 2
;;
-2) VAR2="$2"
shift 2
;;
-3) VAR3="$2"
shift 2
;;
#you could put a 'usage' function here,
#or if your last parameter has no modifier,
#like a mandatory input, it will now be at
#$1
*)
break
;;
esac
done
echo -n "$VAR1, $VAR2 and $VAR3. "
if [ "$SHARENICE" != "true" ]; then
echo "Too bad we don't get along."
else
echo "Good thing we play nice!"
fi

BASH - Refer to parameters using variable

I was trying to make a small script that calculates a binary number to decimal. It works like this:
-Gets '1' or '0' digits as SEPARATE parameters. e.g. "./bin2dec 1 0 0 0 1 1 1".
-For each parameter digit: if it is '1', multiplies it with the corresponding power of 2 (in the above case, the most left '1' will be 64), then adds it in a 'sum' variable.
Here's the code (it is wrong in the noted point):
#!/bin/bash
p=$((2**($#-1))) #Finds the power of two for the first parameter.
sum=0 #The sum variable, to be used for adding the powers of two in it.
for (( i=1; i<=$#; i++ )) #Counts all the way from 1, to the total number of parameters.
do
if [ $i -eq 1 ] # *THIS IS THE WRONG POINT* If parameter content equals '1'...
then
sum=$(($sum+$p)) #...add the current power of two in 'sum'.
fi
p=$(($p/2)) #Divides the power with 2, so to be used on the next parameter.
done
echo $sum #When finished show the 'sum' content, which is supposed to be the decimal equivalent.
My question is in the noted point (line #10, including blank lines). There, Iā€™m trying to check if EACH parameter's content equals 1. How can I do this using a variable?
For example, $1 is the first parameter, $2 is the 2nd and so on. I want it to be like $i, where 'i' is the variable that is increased by one each time so that it matches the next parameter.
Among other things, I tried this: '$(echo "$"$i)' but didn't work.
I know my question is complicated and I tried hard to make it as clear as I could.
Any help?
How about this?
#!/usr/bin/bash
res=0
while [[ $# -gt 0 ]]
do
res=$(($res * 2 + $1))
shift
done
echo $res
$ ./bin2dec 1 0 0 1
9
shift is a command that moves every parameter passed to the script to the left, decreasing it's value by 1, so that the value of $2 is now at $1 after a shift. You can actually a number with shift as well, to indicate how far to shift the variables, so shift is like shift 1, and shift 2 would give $1 the value that used to be at $3.
How about this one?
#!/bin/bash
p=$((2**$#))
while(($#)); do
((sum+=$1*(p/=2)))
shift
done
echo "$sum"
or this one (that goes in the other direction):
#!/bin/bash
while(($#)); do
((sum=2*sum+$1))
shift
done
echo "$sum"
Note that there are no error checkings.
Please consider fedorqui's comment: use echo $((2# binary number )) to convert from binary to decimal.
Also note that this will overflow easily if you give too many arguments.
If you want something that doesn't overflow, consider using dc:
dc <<< "2i101010p"
(setting input radix to 2 with 2i and putting 101010 on the stack and printing it).
Or if you like bc better:
bc <<< "ibase=2;101010"
Note that these need the binary number to be entered as one argument, and not as you wanted with all digits separated. If you really need all digits to be separated, you could also use these funny methods:
Pure Bash.
#!/bin/bash
echo $((2#$(printf '%s' "$#")))
With dc.
#!/bin/bash
dc <<< "2i$(printf '%s' "$#")p"
With bc.
#!/bin/bash
bc <<< "ibase=2;$(printf '%s' "$#")"
Abusing IFS, with dc.
#!/bin/bash
( IFS=; echo "2i$*p" ) | dc
Abusing IFS, with bc.
#!/bin/bash
( IFS=; echo "ibase=2;$*" ) | bc
So we still haven't answered your question (the one in your comment of the OP): And to find out if and how can we refer to a parameter using a variable. It's done in Bash with indirect expansion. You can read about it in the Shell Parameter Expansion section of the Bash reference manual. The best thing is to show you in a terminal:
$ a="hello"
$ b=a
$ echo "$b"
a
$ echo "${!b}"
hello
neat, eh? It's similar with positional parameters:
$ set param1 param2 param3
$ echo "$1, $2, $3"
param1, param2, param3
$ i=2
$ echo "$i"
2
$ echo "${!i}"
param2
Hope this helps!
To reference variables indirectly by name, use ${!name}:
i=2
echo "${!i} is now equivalent to $2"
Try this one also:
#!/bin/bash -
( IFS=''; echo $((2#$*)) )

BASH SCRIPT : How to use a variable inside another variable

I need to store command line arguments passed in an array
my Command is
./test1.sh 2 4 6
Now i need to store 2 4 6 in an array and im using..
s1=$#
"it tells how many arguments are passed."
for (( c=1; c<=$s1; c++ ))
do
a[$c]}=${$c}
I have written ${$c} for taking the arguments value but it is showing bad substition.
This will give you an array with the arguments: args=("$#")
And you can call them like this: echo ${args[0]} ${args[1]} ${args[2]}
bash variable can be indirectly referenced by \$$VARNAME or by ${!VARNAME} in version 2
so your assignment statement must be :
a[$c]=${!c}
or
eval a[$c]=\$$c
You can always pick the first argument and drop it. Use $1 and shift.
c=1
while [ $# -gt 0 ]; do
a[$c]=$1
c=$((c+1))
shift
done

Resources