I have a while loop with an string array and a simple randomize for them.
My problem however is to count how many times the same strings have appear when the loop was running.
Ex :
oc/open string has appeared 3 times
rw/read string has appeared 2 times
oc/close string has appeared 3 times
etc....
At the moment im using if else methods inside the loop, but there must be a better way to count them? Any tips?
function injection {
COUNTER=0
countopen=0
while [ $COUNTER -lt 10 ]; do
module[0]="oc/open"
module[1]="oc/close"
module[2]="rw/read"
module[3]="rw/write"
randModule=$[$RANDOM % ${#module[#]}]
export MODULE=${module[$randModule]}
echo $MODULE
if [ $randModule == 0 ]; then
let countopen++
#let countclose++
#etc
#etc
fi
let COUNTER++
done
echo "Open $countopen"
}
injection
If you can use external commands, use sort and uniq to count the occurrences:
#!/bin/bash
module=( oc/open oc/close rw/read rw/write )
for i in {1..100} ; do
echo ${module[RANDOM % ${#module[#]}]}
done | sort | uniq -c
You can also count them yourself in an associative array:
#!/bin/bash
module=( oc/open oc/close rw/read rw/write )
declare -A count
for i in {1..100} ; do
mod=${module[RANDOM % ${#module[#]}]}
(( ++count[$mod] ))
done
for m in "${module[#]}" ; do
printf '%s %d\n' "$m" "${count[$m]}"
done
Related
In bash I want array let say:
array=(1 2 3)
Then I need a loop for program where
x will be 1,2,3,1,2,3... (from array)
i will be unlimited 1,2,3,4,5,6.... (main loop)
My code:
array=(1 2 3)
while true ; do
((i=i+1))
#screen -dmS plot$i -d /destinatin$x
echo $i $x
sleep 1
done
I do not know how to loop array and set $x to go 1,2,3,1,2,3....
Infinite loops are generally generated using the shell built-in command : which does nothing in its singular form. So if you want to loop infinitely over the elements of a list, you can do the following:
1. The infinite nested while-for loop:
while :; do for i in "${a[#]}"; do echo "${i}"; done; done
2. using an index-reset
i=0; while :; do echo "${a[i]}"; ((i=i+1)); ((i==${#a[#]})) && i=0; done
2. using modulo calculation:
i=0; while :; do echo "${a[i]}"; (( i=(i+1) % ${#a[#]} )); done
3. the infinite for loop with modulo index
for ((i=0;;i++)); do echo "${a[i%${#a[#]}]}"; done
This code should solve your problem:
#!/bin/bash
array=(1 2 3)
i=0
count_of_elements=${#array[#]} #counting the number of array elements
while true; do
rest=$(($i%$count_of_elements)) #counting rest of the division by count of array elements
printf "${array[$rest]}," #dispay result
i=$((i+1))
done
It will be also working if you change your input array (for example if it will be array=(1 2 3 4 5).
I think there is something wrong with the condition and the array.
this is my script
thank you for your time I appreciate.
#!/bin/bash
loop=10
range=20
count=1
declare -a prev
numb=$[1+RANDOM% $range]
prev+=($numb)
echo ===========================
echo $loop DIFFERENT RANDOM NUMBERS
echo ===========================
echo $numb
until [ "$count" -ge "$loop" ]
do
numb=$[1+RANDOM% $range]
if [[ ${prev[#]} -ne $numb ]] ; then
echo $numb
prev+=$numb
((count++))
fi
done
The code attempt to locate previously selected numbers by using the conditions [[ ${prev[#]} -ne $numb ]]. However, bash does not have "in" (or "not in") operators that work on an array and a value.
Consider instead using bash associative arrays. Each used elements is marked by entering a value into the position associated with the selected number
#! /bin/bash
loop=10
range=20
# Associative array prev[N]=1, if N was already printed
declare -A prev
echo ===========================
echo $loop DIFFERENT RANDOM NUMBERS
echo ===========================
for ((count=1 ; count <= loop ; count++)) ; do
numb=$[1+RANDOM% $range]
while [ "${prev[$numb]}" ] ; do
numb=$[1+RANDOM% $range]
done ;
echo $numb
prev[$numb]=1
done
Code using traditional for loop for (( ; ; )) to force loop to run specific number of times.
A quick version using an array -
$: loop=10 min=20 range=20 all=( $( seq $min $((min+range)) ) )
$: while (( loop-- ))
do ndx=$((RANDOM%range))
if (( all[ndx] ))
then echo "${all[ndx]}"
unset "all[ndx]"
else let loop++
fi
done
33
38
27
23
39
32
22
20
36
35
Unsetting each element as used prevents dups.
I'm pretty sure there's a better way... still thinking.
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}
I have to make a script that makes the sum of all processes that have PIDS greater than 20. It kind of works but something is going wrong. I know it's something simple, base thing, but I can't get the logic.
For example I have 2 processes, "1504" and "1405". My result of sum is 15041405. How do I have to re-write sum+=${proc[$i]} to make an actual sum, not string attach?
proc=( $(ps | cut -d ' ' -f1) )
nr=$(ps | cut -d ' ' -f1 | wc -l)
sum=0
for (( i=0 ; i<=nr ; i++)); do
[[ ${proc[$i]} -gt 20 ]] && sum+=${proc[$i]}
done
echo $sum
Use a math context. In POSIX sh:
sum=$(( sum + val ))
...or, also valid POSIX:
: "$(( sum += val ))"
...or, in bash:
(( sum += val ))
You can also use much easier-to-read comparison operations in a math context, rather than using -gt inside of a non-math test context. In bash:
(( ${proc[$i]} >= 20 )) && (( sum += ${proc[$i]} ))
...or in POSIX shell (which doesn't support arrays, and so cannot exactly reproduce your sample code):
: "$(( sum += ( val < 20 ) ? 0 : val ))"
If you were trying to do the (more sensible) operation of counting PIDs, I'd consider an implementation more like the following (bash-only and Linux-only, but considerably more efficient):
count=0
for pid_file in /proc/[0-9]*; do
pid=${pid_file##*/}
(( pid > 20 )) && (( count++ ))
done
printf '%s\n' "$count"
...or, to put more of the effort on the glob engine:
# avoid inflating the result with non-matching globs
shopt -s nullglob
# define a function to avoid overwriting the global "$#" array
count_procs() {
set -- /proc/[3-9][0-9] /proc/[0-9][0-9][0-9]*
echo "$#"
}
# ...used as follows:
count_procs
I tried to create a shell script, which sum the given numbers. If there is no given parameter, then it tries to read the pipe output, but I get an error.
#!/bin/sh
sum=0
if [ $# -eq 0 ]
then
while read data
do
sum=`expr $sum + $data`
done
else
for (( i = 1 ; i <= $#; i++ ))
do
sum=`expr $sum + ${!i}`
done
fi
echo $sum
This works: sum 10 12 13
But this one doesn't: echo 10 12 13| sum
Thanks in advance,
Here you go (assuming bash, not sh):
#!/bin/bash
sum=0
if (( $# == 0 )); then
# Read line by line
# But each line might consist of separate numbers to be added
# So read each line as an array!
while read -a data; do
# Now data is an array... but if empty, continue
(( ${#data[#]} )) || continue
# Convert this array into a string s, with elements separated by a +
printf -v s "%s+" ${data[#]}
# Append 0 to s (observe that s ended with a +)
s="${s}0"
# Add these numbers to sum
(( sum += s ))
done
else
# If elements come from argument line, do the same!
printf -v s "%s+" $#
# Append 0 to s (observe that s ended with a +)
s="${s}0"
# Add these numbers to obtain sum
(( sum = s ))
fi
echo $sum
You can invoke it thus:
$ echo 10 12 13 | ./sum
35
$ ./sum 10 12 13
35
$ # With several lines and possibly empty lines:
$ { echo 10 12 13; echo; echo 42 22; } | ./sum
99
Hope this helps!
Edit. You might also be interested in learning cool stuff about IFS. I've noticed that people tend to confuse # and * in bash. If you don't know what I'm talking about, then you should use # instead of *, also for array subscripts! In the bash manual, you'll find that when double quoted, $* (or ${array[*]}) expands to all the elements of the array separated by the value of the IFS. This can be useful in our case:
#!/bin/bash
sum=0
if (( $# == 0 )); then
# Read line by line
# But each line might consist of separate numbers to be added
# So read each line as an array!
while read -a data; do
# Now data is an array... but if empty, continue
(( ${#data[#]} )) || continue
# Setting IFS=+ (just for the sum) will yield exactly what I want!
IFS=+ sum=$(( sum + ${data[*]} ))
done
else
# If elements come from argument line, do the same!
# Setting IFS=+ (just for the sum) will yield exactly what I want!
IFS=+ sum=$(( $* ))
fi
echo $sum
Gniourf now exits from teacher mode. :-)