get the multi values in prompt and store in an array - bash

I want to get multiple values from user in my bash script.
for example, I need to have a loop to get $x values in prompt like this:
Enter parameter 1 : 10
Enter parameter 2 : 12
Enter parameter 3 : 24
I wrote this code:
x=3
for (( i=1; i<=$x; i++ ))
do
read -p "Enter parameter ${i} : " params
done
for i in ${params[#]}
do
echo $i
done
this code shows the prompt for 3 times and gets 3 different value but when I trying to show the values in the for i in ${params[#]} I will get just latest value.
what should I do?

Another way is to simply append a temp variable to the output array:
#!/usr/bin/env bash
x=3 params=()
for (( i = 1; i <= x; ++i )); do
IFS= read -rp "Enter parameter #$i: " tmp || continue
params+=("$tmp")
done
printf '%s\n' "${params[#]}"

Name the entry in the array that read should save to:
params=()
for (( i=1; i<=$x; i++ ))
do
read -p "Enter parameter ${i} : " 'params[i]'
done
For example:
bash-5.0$ foo=()
bash-5.0$ read foo[1]
ls
bash-5.0$ read foo[2]
ls
bash-5.0$ read foo[3]
bar
bash-5.0$ echo "${foo[#]}"
ls ls bar
bash-5.0$ echo "${!foo[#]}"
1 2 3

Related

How to perform arithmetic in UNIX?

I am trying to build a very basic 4 function arithmetic script in UNIX and it doesn't like my arithmetic statement. I was trying to use 'bash arithmetic' syntax
from this source
http://faculty.salina.k-state.edu/tim/unix_sg/bash/math.html
also as a side note when you reference a variable in UNIX when do you need to use the "$" symbol, and when do you not need to?
#!/bin/bash
str1="add"
str2="sub"
str3="div"
str4="mult"
((int3=0))
((int2=0))
((int1=0))
clear
read -p "please enter the first integer" $int1
clear
read -p "Please enter mathmatical operator'" input
clear
read -p "Please enter the second integer" $int2
if [ "$input" = "$str1" ];
then
((int3 = int1+int2))
echo "$int3"
else
echo "sadly, it does not work"
fi;
I think this is what you want:
#!/bin/bash
str1="add"
str2="sub"
str3="div"
str4="mult"
((int3=0)) # maybe you can explain me in comments why you need a arithmetic expression here to perform an simple assignment?
((int2=0))
((int1=0))
echo -n "please enter the first integer > "
read int1
echo -n "Please enter mathmatical operator > "
read input
echo -n "Please enter the second integer > "
read int2
if [ $input = $str1 ]
then
((int3=int1 + int2))
echo "$int3"
else
echo "sadly, it does not work"
fi
exec $SHELL
You should definitly checkout man bash. It is documented there in which command you need to specify $ or not to reference a variable. But aside from that:
var=123 # variable assignment. no spaces in between
echo $var # fetches/references the value of var. Or in other words $var gets substituted by it's value.
Use bc command
something like this
echo "9/3+12" | bc
You use $ when you want the value of the variable. read, though, expects the name of a variable:
read -p "..." int1
(Technically, you could do something like
name=int1
read -p "..." "$name"
to set the value of int1, because the shell expands name to the string int1, which read then uses as the name.)
Here's a quick once over:
op=( add sub div mult )
int1=0
int2=0
ans=0
clear
read -p "please enter the first integer > " int1
clear
IFS='/'
read -p "Please enter mathmatical operator (${op[*]})> " input
unset IFS
clear
read -p "Please enter the second integer > " int2
case "$input" in
add) (( ans = int1 + int2 ));;
sub) (( ans = int1 - int2 ));;
div) (( ans = int1 / int2 ));; # returns truncated value and might want to check int2 != 0
mult) (( ans = int1 * int2 ));;
*) echo "Invalid choice"
exit 1;;
esac
echo "Answer is: $ans"
You will also want to check that the user enters numbers to :)
Another one
declare -A oper
oper=([add]='+' [sub]='-' [div]='/' [mul]='*')
read -r -p 'Num1? > ' num1
read -r -p "oper? (${!oper[*]}) > " op
read -r -p 'Num2? > ' num2
[[ -n "${oper[$op]}" ]] || { echo "Err: unknown operation $op" >&2 ; exit 1; }
res=$(bc -l <<< "$num1 ${oper[$op]} $num2")
echo "$num1 ${oper[$op]} $num2 = $res"

bash script - iterate over two (or more) ranges simultaneously in for loop

I know that is possible to iterate over few arrays in bash for loop or any command like echo (Brace Expansion).
for i in {1..4} {a..d}
echo $i
# or
echo {1..4} {a..d}
# output: 1 2 3 4 a b c d
But is it possible to iterate over arrays simultaneously to get result like:
# output: 1 a 2 b 3 c 4 d
For example I need to ssh to three range of servers with names like server-a1.com, server-b1.com, server-c1.com and need to perform action on all firsts, than on all seconds and so on.
You can generate all the combinations with
echo {1..4}{a..d}
If you select each fifth, you'll get what you wanted:
all=({1..4}{a..d})
for (( i=0 ; i<${#all[#]} ; i++ )) ; do
(( !(i % 5) )) && echo ${all[i]}
done
sure
for i in {1..4} ; do
for j in {a..d} ; do
echo ssh user#server-${i}${j}.com 'cmd'
done
done
output
ssh user#server-1a.com cmd
ssh user#server-1b.com cmd
ssh user#server-1c.com cmd
ssh user#server-1d.com cmd
ssh user#server-2a.com cmd
ssh user#server-2b.com cmd
ssh user#server-2c.com cmd
. . .
Of course, you get to make your own ssh cmdline using the ${i} and ${j} when needed.
I have used echo ssh ... to be a placeholder for your real processes.
IHTH
This will do what you want:
a=( {1..4} )
b=( {a..d} )
for (( i=0; i<${#a[#]}; i++ )); do
echo -n "${a[i]} ${b[i]} "
done
The range expansion is put into two arrays. The index of the first array are used to refer to both arrays.
#!/bin/bash
a=({1..4}) b=({a..d}) i=0 ### Initial values
for ((i=0; i<4; i++ )) ; do ### Loop over elements
printf '%s %s ' "${a[i]}" "${b[i]}" ### Print pairs of values.
done
echo
$ ./script.sh
1 a 2 b 3 c 4 d

progress indicator for bash script without dialog or pv

I've written a bash script to truncate through a server list and perform various commands. I would like to incorporate a progress indicator like a percent complete while the script is running. I found a script online to perform this but it's not working properly and I am unsure how to utilize it.
The dialog command is not an option here as I am working with nsh
#!/bin/bash
i=0
while [[ $i -lt 11 ]]; do
##
## \r = carriage return
## \c = suppress linefeed
##
echo -en "\r$i%\c\b"
(( i=i+1 ))
sleep 1
done
echo
exit 0
For testing purposes I am only connecting to each server and echoing the hostname.
for i in $(cat serverlist.txt)
do
nexec -i hostname
done
How can I utilize the first code snipped to show the progress while going through the list of servers in the code above?
To keep track of your progress, you'll want to store the list of servers in an array, so you know how many there are:
mapfile -t servers <serverlist.txt # servers is an array
n=${#servers[#]} # how many
i=0
for server in "${servers[#]}"; do
((i++))
progress=$(( i * 100 / n ))
nexec ...
echo "you are ${progress}% complete"
done
write_status() {
done=0
total=$1
columns=${COLUMNS:-100}
# print initial, empty progress bar
for (( i=0; i<columns; i++ )); do printf '-'; done; printf '\r'
# every time we read a line, print a progress bar with one more item complete
while read -r; do
done=$(( done + 1 ))
pct=$(( done * columns / total ))
for ((i=0; i<pct; i++)); do
printf '+'
done
for ((i=pct; i<columns; i++)); do
printf '-'
done
printf '\r'
if (( done == total )); then break; fi
done
}
# read server names into an array; the below is bash 4.x syntax
readarray -t servers <serverlist.txt
# direct FD 4 to the a subshell running the write_status function
exec 4> >(write_status "${#servers[#]}")
for hostname in "${servers[#]}"; do
nexec -i "$hostname" # actually run the command
echo >&4 # ...and notify write_status that it completed
done
If you wanted to parallelize the remote commands, you can do that by replacing the for loop at the bottom with the following:
for hostname in "${servers[#]}"; do
(nexec -i "$hostname"; echo >&4) &
done
wait
If your shell is a version of bash prior to 4.0, then the readarray -t servers <serverlist can be replaced with the following:
servers=( )
while IFS= read -r server; do servers+=( "$server" ); done <serverlist.txt

Bash Script accepting a number, then printing a set of int from 0 to the number entered

I am trying to write a bash script that accepts a number from the keyboard, and then prints a set of integers from 0 to the number entered. I can't figure out how to do it at all.
This is my code:
while [ 1 ]
do
echo -n "Enter a color: "
read user_answer
for (( $user_answer = $user_answer; $user_answer>0; $user_answer--))
echo $user_answer
fi
done
exit
The error I'm recieving is:
number_loop: line 10: syntax error near unexpected token echo'
number_loop: line 10: echo $user_answer'
Assign a separate variable in order to use increment/decrement operators. $user_answer=$user_answer will always be true and it will throw an error when trying to use decrement. Try the following :
#!/bin/bash
while [ 1 ]
do
echo -n "Enter a color: "
read user_answer
for (( i=$user_answer; i>0; i-- ))
do
echo $i
done
done
exit
You missed the do statement between your for and the echo.
bash has many options to write numbers. What you seem to be trying to do is easiest done with seq:
seq $user_answer -1 0
If you want to use your loop, you have to insert a ; do and replace the fi with done, and replace several $user_answer:
for (( i = $user_answer; i>0; i--)); do
echo $i
done
(btw: I assumed that you wanted to write the numbers in reverse order, as you are going backwards in your loop. Forwards is even easier with seq:
seq 0 $user_input
)
This is where a c-style loop works particularly well:
#!/bin/bash
for ((i = 1; i <= $1; i++)); do
printf "%s\n" "$i"
done
exit 0
Example
$ bash simplefor.sh 10
1
2
3
4
5
6
7
8
9
10
Note: <= is used as the for loop test so it 10 it iterates 1-10 instead of 0-9.
In your particular case, iterating from $user_answer you would want:
for (( i = $user_answer; i > 0; i--)); do
echo $i
done
The for loop is a bash internal command, so it doesn't fork a new process.
The seq command has a nice, one-line syntax.
To get the best of the twos, you can use the { .. } syntax:
eval echo {1..$answer}

How to add a variable to count the number of times the script has been called?

I am writing a script to add data to a file after the script has been run.
Since I need to mail the data forward, I need a variable to assign the number of times the script has been called.
I am using a temp file which stores the value of count. Every time the script is called the variable temp is called, the value incremented and stored in count. The incremented value is than stored back to the temp.
count=$((temp + 1))
echo $count > temp
printf "%d\t%10s\t" "$count" "$datet" >> table
this is the code i'm using, but temp is not increasing...?
Just read the previous value before anything else:
temp=$(cat temp)
count=$((temp + 1))
echo "$count" > temp
printf "%d\t%10s\t" "$count" "$datet" >> table
Test
$ echo "0" > temp
$ ./a
$ cat temp
1
$ ./a
$ cat temp
2
You have to use cat temp to get the value inside the file temp.
But I would suggest to use a variable for the temporary file for better code readeablility:
TMP=/tmp/counter
if [ ! -f $TMP ]; then echo "0">$TMP; fi
count=$(($(cat $TMP) + 1))
echo $count > $TMP
printf "%d\t%10s\t" "$count" >> /tmp/somelogfile
Or in one line, if you don't need the count in a variable:
echo "$(($(cat $TMP) + 1))">$TMP
Then you can later use:
echo $(cat $TMP)

Resources