numbers as command-line arguments and prints the count - bash

I want to take numbers as command-line arguments and prints the count of
numbers that end with 0, 1, 2, etc. up to 5.
Example:
bash test.sh 12 14 12 15 14
Expected Output:
Digit_ends_with count
0 0
1 0
2 2
3 0
4 2
5 1
Mt Attempt:
read -a integers for i in ${integers[#]} do if [[ grep -o '[0-9]' $i ]] count=$(grep -c $i) if [ "$count" -ge "0" ] then
echo "Digit_ends_with" $i echo -e "Count ""$count" fi fi done
But this is not working. How I can achieve this requirement?

#!/bin/bash
echo "Digit_ends_with count" #print table header
for argument in "$#" #loop over all given arguments
do
current=$(echo "$argument" | tail -c 2 ) #get last digit
if (( "$current" <= 5 )) #check if lower than 6
then
echo "$current" #echo if true
fi
done | sort | uniq -c | sed -E 's/\s+//' | sed -E 's/([0-9]+).?([0-9]+)/\2\t\t\1/' #sort, count, remove leading spaces and switch the fields
Example:
╰─$ ./test.sh 188 182 182 12 13 14 18 15 16 17 18 19 10 0 0 0 0 0 0 0 0 0 0
Digit_ends_with count
0 11
2 3
3 1
4 1
5 1

Would you please try the following:
#!/bin/bash
for i in "$#"; do # loop over arguments
(( count[i % 10]++ )) # index with the modulo 10
done
printf "%s %s\n" "Digit_ends_with" "count" # header line
for (( i = 0; i < 6; i++ )); do # loop between 0 and 5
printf "%d\t\t%d\n" "$i" "${count[$i]}" # print the counts
done
Result of ./test.sh 12 14 12 15 14:
Digit_ends_with count
0 0
1 0
2 2
3 0
4 2
5 1

Related

Removing Leading 0 from a number causes value to change [duplicate]

This question already has answers here:
Shell equality operators (=, ==, -eq)
(4 answers)
Closed 1 year ago.
I do not understand what is happening here, based on Convert string into integer in bash script - "Leading Zero" number error, I am able to convert the string into integer. I can even do addition. However, when I compare the numbers it gives me the wrong comparison. Refer to example below
Here's an example
array=(02 08 10 11 23 52)
for i in 0 1 2 3 4 5
do
if [[ ${array[i]#0} > 15 ]]
then
echo "${array[i]#0} is greater than 15"
else
echo "${array[i]#0} is less than 15"
fi
done
Output
2 is greater than 15
8 is greater than 15
10 is less than 15
11 is less than 15
23 is greater than 15
52 is greater than 15
Either change [[ ]] to (( )) to use > or change the operator to -gt to do integer comparisons inside of [[ ]]
First:
#!/bin/bash
array=(02 08 10 11 23 52)
for i in 0 1 2 3 4 5
do
if (( ${array[i]#0} > 15 ))
then
echo "${array[i]#0} is greater than 15"
else
echo "${array[i]#0} is less than 15"
fi
done
Second:
#!/bin/bash
array=(02 08 10 11 23 52)
for i in 0 1 2 3 4 5
do
if [[ ${array[i]#0} -gt 15 ]]
then
echo "${array[i]#0} is greater than 15"
else
echo "${array[i]#0} is less than 15"
fi
done
If you use < > == inside of [ ] or [[ ]] is a string comparison operator. The string "02" is in fact less than the string "15":
% [[ 02 > 15 ]]; echo $?
1 # '1' means false and '0' means true
BUT you are stripping the leading "0" with #0:
% s="02"
% echo ${s#0}
2
So now you are comparing the STRING "2" to the STRING "15":
% [[ 2 > 15 ]]; echo $?
0
% [[ ${s#0} > 15 ]]; echo $?
0
Which give the surprising result you are seeing...
See this answer for an expanded set of examples.

How to get specific words by index in a line bash

I have a script that takes parameters such as:
script.sh 1 3
I want to then go through a text file and print out the first and third words from each line. I have simply no idea how to do this. If anyone could help I'd really appreciate it...
This is what I currently have:
counter=0
wordcounter=0
param=$(echo "$3" | tr , " ")
words(){
for word in $1; do
for col in $param; do
if [ $wordcounter -eq $col ]; then
echo $word
fi
done
done
wordcounter=$((wordcounter + 1))
}
eachline() {
newline=$(echo "$1" | tr , " ")
for word in $newline; do
if [ $counter -gt 3 ]; then
echo "$word"
fi
counter=$((counter + 1))
done
if [ $counter -gt 0 ]; then
words "$newline"
fi
counter=$((counter + 1))
}
while read line; do
eachline $line
done < company/employee.txt
Use awk:
$ awk '{print $1 " " $3}' file
for file:
1 2 3 4 5
6 7 8 9 0
Output is:
1 3
6 8
In bash script:
#!/bin/bash
awk_command="{print";
for i in "$#"; do
awk_command="${awk_command} \$${i} \" \"";
done
awk_command="${awk_command}}";
awk "$awk_command" file
With this script you can pass any number of indexes:
For 1 and 2:
$ ./script.sh 1 2
1 2
6 7
For 1, 2 and 5:
$ ./script.sh 1 2 5
1 2 5
6 7 0

Bash parse list for prime numbers.

I want to parse a list of hexas that i get from a file after parsing them for unique hexas and sorting them.
The list looks like this:
1 0xb6e38000
8 0xb6f66000
5 0xb6f69000
1 0xb6f6c000
3 0xb6fd4000
1 0xb6ff7000
2 0xb6ff8000
4 0xb6ffa000
1 0xb6ffb000
Now what i want to do, is refine it even more so that i get only the hexas with prime numbers in front, like this:
1 0xb6e38000
5 0xb6f69000
1 0xb6f6c000
3 0xb6fd4000
1 0xb6ff7000
1 0xb6ffb000
The command i have been using is this:
sort | uniq -c | grep " 1 0x"
But this command lists only the ones that appear only once in the file.
Can anyone help me "sort" this out ?
Original answer for prime numbers
As mentioned in the comments to the question, 1 is not prime, but in the description of desired output you list the lines starting with 1. However, if you actually want to filter out the lines with prime numbers in the first column, then the following script will help:
#!/bin/bash -
[ $# -gt 0 ] && source_file="$1"
: ${source_file:=/tmp/source-file}
function is_prime {
declare -i n="$1"
if [ $n -le 1 ]; then
return 1
elif [ $n -le 3 ]; then
return 0
elif [[ $(( $n % 2 )) == 0 || $(( $n % 3 )) == 0 ]]; then
return 1
fi
i=5
while [[ $(( $i * $i )) -le $n ]]; do
if [[ $(( $n % $i )) == 0 || $(( $n % ($i + 2) )) == 0 ]]; then
return 1
fi
(( i += 6 ))
done
return 0
}
while read -a line; do
# We accept only two or more columns
[ ${#line[#]} -ge 2 ] || continue;
if is_prime ${line[0]}; then
echo $line
else
echo >&2 "skipping ${line[*]} as ${line[0]} is not prime"
fi
done < "$source_file"
In this script we define is_prime function which returns zero (success status), if the first argument ($1) is a prime number. Non-zero means non-prime number. The algorithm is a version of this pseudo-code translated into Bash.
Then we read $source_file line by line with the while loop where we put the columns into the line array: read -a line. Then we check if is_prime ${line[0]} command exits with a success code (zero) and output the line if it is. Otherwise, we print a message to the standard error (echo >&2).
The script accepts optional argument for the source file path. It assigns $source_file to /tmp/source-file, if the first argument is missing.
Script usage
Save the above-mentioned code in script.sh file.
Call bash script.sh /path/to/source-file >filtered 2>errors
Examine the contents of filtered and errors files. The first will contain the lines filtered out from the source file.
The output files will look like the following:
filtered
5 0xb6f69000
3 0xb6fd4000
2 0xb6ff8000
errors
skipping 1 0xb6e38000 as 1 is not prime
skipping 8 0xb6f66000 as 8 is not prime
skipping 1 0xb6f6c000 as 1 is not prime
skipping 1 0xb6ff7000 as 1 is not prime
skipping 4 0xb6ffa000 as 4 is not prime
skipping 1 0xb6ffb000 as 1 is not prime
Update for odd numbers
i sould have mentioned this from the start, i need odd number lines to
show up, not prime. My bad. – biotic
It's easy to detect if a number is odd with an expression like if [[ $(( $n % 2 )) != 0 ]]. The expression checks if the remainder by 2 is not equal to zero, i.e. applies the modulo operation. If the remainder is zero, then the number is even, otherwise odd, of course.
The full script:
#!/bin/bash -
[ $# -gt 0 ] && source_file="$1"
: ${source_file:=/tmp/source-file}
while read -a line; do
# We accept only two or more columns
[ ${#line[#]} -ge 2 ] || continue;
declare -i n=${line[0]}
if [[ $(( $n % 2 )) != 0 ]]; then
echo ${line[*]}
else
echo >&2 "skipping ${line[*]} as ${line[0]} is even"
fi
done < "$source_file"
As for above, you run bash script.sh /path/to/source-file >filtered 2>errors
Sample output:
filtered
1 0xb6e38000
5 0xb6f69000
1 0xb6f6c000
3 0xb6fd4000
1 0xb6ff7000
1 0xb6ffb000
errors
skipping 8 0xb6f66000 as 8 is even
skipping 2 0xb6ff8000 as 2 is even
skipping 4 0xb6ffa000 as 4 is even

Reading input in bash from stdin

This is my input:
99116784 12 12 0 p
99116784 12 12 0
This is the code i have
while read line || [[ -n "$line" ]];
do for var in $line
do echo $var
done
done
If I then print $line in the while, the p gets printed
Just doing echo $var doesnt print or know the p is there, anyone knows why?
I reorganised your code and it works in bash on my Mac.
#!/bin/bash
while read line || [[ -n "$line" ]];
do
for var in $line
do
echo $var
done
done
exit 0
running it
./test.sh
99116784 12 12 0 p
99116784
12
12
0
p
99116784 12 12 0
99116784
12
12
0

Converting from for loop to while if fi loop UNIX

I'm trying to figure out how to convert the following for loop into a while loop or until loop including if fi for UNIX Here's the code...
#!/bin/bash
if [ $# -gt 0 ] ; then
start=$1
end=$3
step=$2
for x in `seq $start $step $end` ; do
number $x
done
else
echo Enter at least one number on the command line.
fi
Any help is greatly appreciated!
(( x = start ))
until (( x > end ))
do
number $x
(( x += step ))
done
For a loop like this, it's easiest to use the numeric-style for loop:
for ((x=start; x<=end; x+=step)); do
number $x
done
Using seq is good as it has some default values for step and end value. I added this to the if if only one, or two arguments are given. Also it handles the situation if the given argument are not numbers (default values are 0). Also an endless loop may encounter if step is zero. If You remove this checking it will work the same way as the seq version.
if [ $# -gt 0 ] ; then
x=$(($1+0))
end=$((${3:-$x}+0))
step=$((${2:-1}+0))
[ $step -eq 0 ] && echo "Step is zero!" && exit 1
while [ $x -le $end -a $step -gt 0 -o $x -ge $end -a $step -lt 0 ]; do
echo $x
x=$((x+step))
done
else
echo Enter at least one number on the command line.
fi
Change echo to number in the while loop if you would like to use!
Examples:
$ ./e.sh 1
1
$ seq 1
1
$ ./e.sh 1 1 3
1
2
3
$ seq 1 1 3
1
2
3
$ ./e.sh 1 -1 3
$ seq 1 -1 3
$ ./e.sh 3 -1 1
3
2
1
$ seq 3 -1 1
3
2
1
$ ./e.sh 1 0 1
Step is zero!
$ seq 1 0 1|head
1
1
1
... (endlessly)
$ ./e.sh 1 .1 1.2
./e.sh: line 3: 1.2+0: syntax error: invalid arithmetic operator (error token is ".2+0")
$ seq 1 .1 1.2
1.0
1.1
1.2
Bash arithmetic is not working with floating point numbers, but seq does. If You need this feature You could use the x=$(echo $x + $step|bc) format. Also $((...)) should replaced by $(...|bc) like lines.
In Bash 4 you can also do it like this:
for i in {$start..$stop..$step}; do
echo $i
done

Resources