Bash script add numbers as received in arguments - linux

The requirement is that when the script is called as below:
my_script.sh 1 2 3 4
The script should be able to add these numbers and print sum at the end.
num=0
for arg in "$#"
do
for number in $arg
do
(( num += $number ))
done
done
I've written the script as above but not getting the desired output.

Your script doesn't output anything. Try adding
echo $num
at the end.
BUT your script can be simplified. As written, your script would work for input like
my_script.sh 1 '2 3' 4
as well, as it first loops over the arguments (for arg in "$#"), but then also loops over all the words in each argument (for number in $arg). You don't need the second loop.
Also, naming the result $sum seems to better explain its purpose. So, you can just type
#!/bin/bash
sum=0
for number in "$#" ; do
(( sum += number ))
done
echo $sum

Related

How to write a bash script to label each argument like this:

$ bash argcnt.sh this is a "real live" test
is
real live
(to display only paired arguments)
Because, I know only in this way:
#!/bin/bash
echo "$2"
echo "$4"
It seems like you want to print every other argument given to the script. You could then create a loop over $#:
#!/bin/bash
# idx will be 2, 4, 6 ... for as long as it's less than the number of arguments given
for ((idx = 2; idx < ${##}; idx += 2))
do
# variable indirection below:
echo "${!idx}"
done
Note: You can use $# instead of ${##} to get the number of elements in $# too. I don't know which one that is preferred by people in general.
If what you want is print every other argument, starting from the second, you can use shift:
$ cat argcnt
#!/bin/bash
while shift; do printf '%s\n' "$1"; shift; done
$ ./argcnt this is a "real live" test foo
is
real live
foo

how to generate sequence of ones and zeros in bash script

I am trying to generate a sequence of ones and zeros in using bash script.
#!/bin/bash
clock=1
n=1
# continue until $n equals 5
while [ $n -le 5 ]
do
echo "$clock"
n=$(( n+1 )) # increments $n
clock=$(~clock)
done
Expected output:
1
0
1
0
1
Output generated from the above code:
I am getting error from this line: clock=$(~clock)
Error: ~clock: command not found
If you want to generate sequence with 1 and 0 alternatively, you can use
#!/bin/bash
clock=1
n=1
# continue until $n equals 5
while [ $n -le 5 ]
do
echo "$clock"
n=$(( n+1 ))
# increments $n
clock=$((clock+1))
clock=$((clock%2))
done
If you want to generate random sequence of 1 and 0s, you can use
#!/bin/bash
n=1
# continue until $n equals 5
while [ $n -le 5 ]
do
echo "$((RANDOM%2))"
n=$(( n+1 ))
done
Problem is in this line:
clock=$(~clock)
Here bash is trying to run anything inside $(...) as a command (it is called command substitution).
Using ~clock is also incorrect as it will only do bitwise negation and will not produce 1 and 0 as you are expecting.
You can use this script to get alternate 1 and 0 printed:
#!/bin/bash
clock=1
# continue until $n equals 5
for ((n=0; n<5; n++))
do
echo "$clock"
clock=$((1 - clock))
done
Another short way to do it
#!/bin/bash
for i in {1..5}
do
echo $((i%2))
done
Or slightly less concise but easier to configure with variables to define the loop :
#!/bin/bash
for ((i=1;i<=5;i++))
do
echo $((i%2))
done
Another one, just for fun (would not do that in a read script, more like a little puzzle to figure out why it works). Remove the "false" line to begin with 0.
#!/bin/bash
false
for i in {1..5}
do
echo $? ; [[ $_ != 0 ]]
done
You can utilize the following trick using the brace expansion.
echo {,,,,}{1,0}
This one will generate 5 pairs of ones and zeroes.

run shell script with arguments manipulation

I need to get three arguments by test.ksh script
as the following
./test.ksh 12 34 AN
is it possible to set the argument by counter for example ?
for get_arg 1 2 3
do
my_array[get_arg]=$$get_arg
print ${my_array[get_arg]}
done
in this example I want to get three arguments from the user by loop counter "$$get_arg"
in place of $1 $2 $3
is it possible? and how ?
my_array=("$#")
for i in 0 1 2
do
echo "${my_array[$i]}"
done
This assigns all the arguments to array my_array; the loop then selects the first three arguments for echoing.
If you're sure you want the first three arguments in the array, you could use:
my_array=("$1" "$2" "$3")
If you want the 3 arguments at positions 1, 2, 3 in the array (rather than 0, 1, 2), then use:
# One or the other but not both of the two assignments
my_array=("dummy" "$#")
my_array=("dummy" "$1" "$2" "$3")
for i in 1 2 3
do
echo "${my_array[$i]}"
done
bash has a special variable
$#
which contains the arguments of the script it currently executes. I think this is what your'e looking for:
for arg in $# ; do
# code
done
Edit:
My bad ksh:
for arg;do
print $arg
done
Original Post:
Use shift to iterate through shell script parameters:
# cat test.sh
#!/bin/bash
while [ "$1" != "" ]; do
echo $1
shift
done
test run:
# ./test.sh arg1 monkey arg3
arg1
monkey
arg3
source
Even you don't need in $#, this would work the same:
#!/bin/bash
i=0
for arg; do
my_array[i]="$arg"
echo "${my_array[i]}"
(( i++ ))
done
That is,
if in words is not present, the for command executes the commands
once for each positional parameter that is set, as if in $# had been
specified.

Bash reading txt file and storing in array

I'm writing my first Bash script, I have some experience with C and C# so I think the logic of the program is correct, it's just the syntax is so complicated because apparently there are many different ways to write the same thing!
Here is the script, it simply checks if the argument (string) is contained in a certain file. If so it stores each line of the file in an array and writes an item of the array in a file. I'm sure there must be easier ways to achieve that but I want to do some practice with bash loops
#!/bin/bash
NOME=$1
c=0
#IF NAME IS FOUND IN THE PHONEBOOK THEN STORE EACH LINE OF THE FILE INTO ARRAY
#ONCE THE ARRAY IS DONE GET THE INDEX OF MATCHING NAME AND RETURN ARRAY[INDEX+1]
if grep "$NOME" /root/phonebook.txt ; then
echo "CREATING ARRAY"
while read line
do
myArray[$c]=$line # store line
c=$(expr $c + 1) # increase counter by 1
done < /root/phonebook.txt
else
echo "Name not found"
fi
c=0
for i in myArray;
do
if myArray[$i]="$NOME" ; then
echo ${myArray[i+1]} >> /root/numbertocall.txt
fi
done
This code returns the only the second item of myArray (myArray[2]) or the second line of the file, why?
The first part (where you build the array) looks ok, but the second part has a couple of serious errors:
for i in myArray; -- this executes the loop once, with $i set to "myArray". In this case, you want $i to iterate over the indexes of myArray, so you need to use
for i in "${!myArray[#]}"
or
for ((i=0; i<${#a[#]}; i++))
(although I generally prefer the first, since it'll work with noncontiguous and associative arrays).
Also, you don't need the ; unless do is on the same line (in shell, ; is mostly equivalent to a line break so having a semicolon at the end of a line is redundant).
if myArray[$i]="$NOME" ; then -- the if statement takes a command, and will therefore treat myArray[$i]="$NOME" as an assignment command, which is not at all what you wanted. In order to compare strings, you could use the test command or its synonym [
if [ "${myArray[i]}" = "$NOME" ]; then
or a bash conditional expression
if [[ "${myArray[i]}" = "$NOME" ]]; then
The two are very similar, but the conditional expression has much cleaner syntax (e.g. in a test command, > redirects output, while \> is a string comparison; in [[ ]] a plain > is a comparison).
In either case, you need to use an appropriate $ expression for myArray, or it'll be interpreted as a literal. On the other hand, you don't need a $ before the i in "${myArray[i]}" because it's in a numeric expression context and therefore will be expanded automatically.
Finally, note that the spaces between elements are absolutely required -- in shell, spaces are very important delimiters, not just there for readability like they usually are in c.
1.-This is what you wrote with small adjustments
#!/bin/bash
NOME=$1
#IF NAME IS FOUND IN THE PHONE-BOOK **THEN** READ THE PHONE BOOK LINES INTO AN ARRAY VARIABLE
#ONCE THE ARRAY IS COMPLETED, GET THE INDEX OF MATCHING LINE AND RETURN ARRAY[INDEX+1]
c=0
if grep "$NOME" /root/phonebook.txt ; then
echo "CREATING ARRAY...."
IFS= while read -r line #IFS= in case you want to preserve leading and trailing spaces
do
myArray[c]=$line # put line in the array
c=$((c+1)) # increase counter by 1
done < /root/phonebook.txt
for i in ${!myArray[#]}; do
if myArray[i]="$NOME" ; then
echo ${myArray[i+1]} >> /root/numbertocall.txt
fi
done
else
echo "Name not found"
fi
2.-But you can also read the array and stop looping like this:
#!/bin/bash
NOME=$1
c=0
if grep "$NOME" /root/phonebook.txt ; then
echo "CREATING ARRAY...."
readarray myArray < /root/phonebook.txt
for i in ${!myArray[#]}; do
if myArray[i]="$NOME" ; then
echo ${myArray[i+1]} >> /root/numbertocall.txt
break # stop looping
fi
done
else
echo "Name not found"
fi
exit 0
3.- The following improves things. Supposing a)$NAME matches the whole line that contains it and b)there's always one line after a $NOME found, this will work; if not (if $NOME can be the last line in the phone-book), then you need to do small adjustments.
!/bin/bash
PHONEBOOK="/root/phonebook.txt"
NUMBERTOCALL="/root/numbertocall.txt"
NOME="$1"
myline=""
myline=$(grep -A1 "$NOME" "$PHONEBOOK" | sed '1d')
if [ -z "$myline" ]; then
echo "Name not found :-("
else
echo -n "$NOME FOUND.... "
echo "$myline" >> "$NUMBERTOCALL"
echo " .... AND SAVED! :-)"
fi
exit 0

Check that there are at least two arguments given in a bash script

I am trying to write a script that mimics cp where there is a source and destination input. How can I count the number of arguments given on the command line?
For example:
./myscript src dest
How can I check that at least 2 things were given?
Use the $# special variable. Its value is the number of arguments. So if you have a script that contains only:
echo $#
and execute it like this:
thatscript foo bar baz quux
It'll print 4.
In your case you may want to do something like:
if [ $# -lt 2 ]; then
# TODO: print usage
exit 1
fi
Going by the requirement from the question that the arguments should contain "at least 2 things", I think it might be more accurate to check:
if (( $# < 2 )); then
# TODO: print usage
exit 1
fi
Using arithmetic expansion (( )) will prevent this from hitting exit 1 for any value not equal to 2.
If you use if [ $# -ne 2 ]; it will trigger the conditional for any number of arguments other than 2.
Edit: It looks like the accepted answer has been updated to include an equivalent (and more portable?) example of this.

Resources