Finding highest value of variable - bash

So i am trying to find the highest value of the variable. For example o have this:
var1=14
var2=15
var3=16
I want to find the biggest value which is var 3 and save it somewhere. Is there a way to do that?
Something like this:
tmp=`sort -n $var1 $var2 $var3 ` (this is an example)

You'll need to get those numbers into an array, from there it's just:
a=(14 15 16) # Example array
IFS=$'\n'
echo "${a[*]}" | sort -nr | head -n1

This will find the max, by the variable names
#!/bin/bash
maxvarname() {
for i; do
echo "${!i} $i"
done | sort -nr | sed -n '1s/.* \(.*\)/\1/p'
}
#MAIN
#the variables
var1=14
var2=15
var3=16
vname=$(maxvarname var1 var2 var3) #note, arguments are the NAMES (not values e.g. $var1) - without $
echo "Max value is in the variable named: '$vname' and its value is: ${!vname}"
it prints:
Max value is in the variable named: 'var3' and its value is: 16

max=$(echo $var{1,2,3} | tr ' ' '\n' | sort -nr | head -1)

Check below solution if you want to find the maximum value of a variable -
$ cat f
var4=18
var1=14
var2=15
var3=16
$ max=$(sort -t'=' -nrk2 f|head -1)
$ echo $max
var4=18

Related

How to implement a counter using dictionary in Bash

#!/bin/bash
clear
Counter() {
declare -A dict
while read line; do
if [[ -n "${dict[$line]}" ]]; then
((${dict[$line]}+1))
else
dict["$line"]=1
fi
done < /home/$USER/.bash_history
echo ${!dict[#]} ${dict[#]}
}
Counter
I'm trying to write a script that counts the most used commands in your bash history using dictionary to store commands as keys and amount of times you used a command as a value but my code fails successefully.
Can you help me write the code that works.
Python code for reference:
def Counter(file):
dict = {}
for line in file.read().splitlines():
if line not in dict:
dict[line] = 1
else:
dict[line] += 1
for k, v in sorted(dict.items(), key=lambda x: x[1]):
print(f"{k} was used {v} times")
with open("/home/igor/.bash_history") as bash:
Counter(bash)
Output:
echo $SHELL was used 11 times
sudo apt-get update was used 14 times
ls -l was used 14 times
ldd /opt/pt/bin/PacketTracer7 was used 15 times
zsh was used 17 times
ls was used 26 times
There's no need to initialize the value to 1 for the first occurrence. Bash can do that for you.
The problem is you can't use an empty string as a key, so prepend something and remove it when showing the value.
#! /bin/bash
Counter() {
declare -A dict
while read line; do
c=${dict[x$line]}
dict[x$line]=$((c+1))
done < /home/$USER/.bash_history
for k in "${!dict[#]}" ; do
echo "${dict[$k]}"$'\t'"${k#x}"
done
}
Counter | sort -n
Python code for reference:
To count occurrences of lines, in shell you would typically do sort | uniq -c | sort you would do:
sort ~/.bash_history | uniq -c | sort -rn | head

Joining 2 variables under loop

I'm trying to join 2 variables under loop but I cant get it to work..
My script lists newly added movies. I'm trying to make an output in excel that is clickable. Long story short, I need the script to list the 2 variables like this:
ab
ab
Right now it's doing this
a
a
b
b
This is the code
NEW_MOVIES_DIRLIST=''
for i in $(seq 1 ${NEW_MOVIES_COUNT}); do
MOVIE_PATH=$(echo -e "${NEW_MOVIES_LIST}" | sed -n "${i}p")
NEW_MOVIES_DIRLIST+="$(dirname "${MOVIE_PATH}")/\n"
done
LINKNAME=$(echo -e "${NEW_MOVIES_DIRLIST}" | sed -r 's,FOLTERS_TO_BE_SCANNED/HDD-EXTENDED.-SD./,,g')
NEW_MOVIES_DIRLIST=$(echo -e "${NEW_MOVIES_DIRLIST}" | sed '/^$/d')
NEW_MOVIES_COUNT=$(echo "${NEW_MOVIES_DIRLIST}" | wc -l)
NEW_MOVIES_LIST=''
for ((i = 0; i < ${NEW_MOVIES_COUNT}; i++))
do echo ${NEW_MOVIES_DIRLIST}${LINKNAME}
done
echo "Found ${NEW_MOVIES_COUNT} movies and ${NEW_SERIALS_COUNT} serials!"
${NEW_MOVIES_LIST}
The 2 variables are NEW_MOVIES_DIRLIST and LINKNAME, I can't join them when I run it. Any idea why?
You are adding a newline to the end of the string in the sed. So strip the newline before you display it.
newline=$'\n'
echo "${NEW_MOVIES_DIRLIST//$newline//}$LINKNAME"

Sorting and printing a file in bash UNIX

I have a file with a bunch of paths that look like so:
7 /usr/file1564
7 /usr/file2212
6 /usr/file3542
I am trying to use sort to pull out and print the path(s) with the most occurrences. Here it what I have so far:
cat temp| sort | uniq -c | sort -rk1 > temp
I am unsure how to only print the highest occurrences. I also want my output to be printed like this:
7 1564
7 2212
7 being the total number of occurrences and the other numbers being the file numbers at the end of the name. I am rather new to bash scripting so any help would be greatly appreciated!
To emit only the first line of output (with the highest number, since you're doing a reverse numeric sort immediately prior), pipe through head -n1.
To remove all content which is not either a number or whitespace, pipe through tr -cd '0-9[:space:]'.
To filter for only the values with the highest number, allowing there to be more than one:
{
read firstnum name && printf '%s\t%s\n' "$firstnum" "$name"
while read -r num name; do
[[ $num = $firstnum ]] || break
printf '%s\t%s\n' "$num" "$name"
done
} < temp
If you want to avoid sort and you are allowed to use awk, then you can do this:
awk '{
if($1>maxcnt) {s=$1" "substr($2,10,4); maxcnt=$1} else
if($1==maxcnt) {s=s "\n"$1" "substr($2,10,4)}} END{print s}' \
temp

Get variable value by finding keyword in unix environment

In UNIX environment, I have a file.txt that contains following details:
Data recording started:
0001100 Matched at 412090
0001101 Mismatched at 414798
0001102 Matched at 420007
0001103 Mismatched at 420015
Job completed
How do I can get the first Matched value by searching "Matched" (line 2) word and also for the first "Mismatched" (line 3)
Find the difference between them and store as a variable, "dif"
The result is Matched minus Mismatched, so it cannot find the data by specify line number, i.e. find line 3 last integers minus line 2 last integers, because the mismatched may come at first like following:
Data recording started:
0001100 Mismatched at 412090
0001101 Matched at 414798
0001102 Mismatched at 420007
0001103 Matched at 420015
Job completed
One way:
echo $((
$(grep Matched input | head -1 | sed 's/.*at //')
- $(grep Mismatched input | head -1 | sed 's/.*at //')
))
or using only sed:
echo $((
$(sed -n 's/.*Matched.*at //p' input | head -1)
- $(sed -n 's/.*Mismatched.*at //p' input | head -1)
))
Output
-2708
We can use grep -m 1 to kick away head.
dif=$((
$(grep -m 1 'Matched' a.txt | sed 's/.*at \([0-9]*\).*/\1/')
- $(grep -m 1 'Mismatched' a.txt | sed 's/.*at \([0-9]*\).*/\1/')
))
echo $dif

Bash associative array sorting by value

I get the following output:
Pushkin - 100500
Gogol - 23
Dostoyevsky - 9999
Which is the result of the following script:
for k in "${!authors[#]}"
do
echo $k ' - ' ${authors["$k"]}
done
All I want is to get the output like this:
Pushkin - 100500
Dostoyevsky - 9999
Gogol - 23
which means that the keys in associative array should be sorted by value. Is there an easy method to do so?
You can easily sort your output, in descending numerical order of the 3rd field:
for k in "${!authors[#]}"
do
echo $k ' - ' ${authors["$k"]}
done |
sort -rn -k3
See sort(1) for more about the sort command. This just sorts output lines; I don't know of any way to sort an array directly in bash.
I also can't see how the above can give you names ("Pushkin" et al.) as array keys. In bash, array keys are always integers.
Alternatively you can sort the indexes and use the sorted list of indexes to loop through the array:
authors_indexes=( ${!authors[#]} )
IFS=$'\n' authors_sorted=( $(echo -e "${authors_indexes[#]/%/\n}" | sed -r -e 's/^ *//' -e '/^$/d' | sort) )
for k in "${authors_sorted[#]}"; do
echo $k ' - ' ${authors["$k"]}
done
Extending the answer from #AndrewSchulman, using -rn as a global sort option reverses all columns. In this example, authors with the same associative array value will be output by reverse order of name.
For example
declare -A authors
authors=( [Pushkin]=10050 [Gogol]=23 [Dostoyevsky]=9999 [Tolstoy]=23 )
for k in "${!authors[#]}"
do
echo $k ' - ' ${authors["$k"]}
done | sort -rn -k3
will output
Pushkin - 10050
Dostoyevsky - 9999
Tolstoy - 23
Gogol - 23
Options for sorting specific columns can be provided after the column specifier.
i.e. sort -k3rn
Note that keys can be specified as spans. Here -k3 happens to be fine because it is the final span, but to use only column 3 explicitly (in case further columns were added), it should be specified as -k3,3,
Similarly to sort by column three in descending order, and then column one in ascending order (which is probably what is desired in this example):
declare -A authors
authors=( [Pushkin]=10050 [Gogol]=23 [Dostoyevsky]=9999 [Tolstoy]=23 )
for k in "${!authors[#]}"
do
echo $k ' - ' ${authors["$k"]}
done | sort -k3,3rn -k1,1
will output
Pushkin - 10050
Dostoyevsky - 9999
Gogol - 23
Tolstoy - 23
The best way to sort a bash associative array by VALUE is to NOT sort it.
Instead, get the list of VALUE:::KEYS, sort that list into a new KEY LIST, and iterate through the list.
declare -A ADDR
ADDR[192.168.1.3]="host3"
ADDR[192.168.1.1]="host1"
ADDR[192.168.1.2]="host2"
KEYS=$(
for KEY in ${!ADDR[#]}; do
echo "${ADDR[$KEY]}:::$KEY"
done | sort | awk -F::: '{print $2}'
)
for KEY in $KEYS; do
VAL=${ADDR[$KEY]}
echo "KEY=[$KEY] VAL=[$VAL]"
done
output:
KEY=[192.168.1.1] VAL=[host1]
KEY=[192.168.1.2] VAL=[host2]
KEY=[192.168.1.3] VAL=[host3]
Do something with unsorted keys:
for key in ${!Map[#]}; do
echo $key
done
Do something with sorted keys:
for key in $(for x in ${!Map[#]}; do echo $x; done | sort); do
echo $key
done
Stored sorted keys as array:
Keys=($(for x in ${!Map[#]}; do echo $x; done | sort))
If you can assume the value is always a number (no spaces), but want to allow for the possibility of spaces in the key:
for k in "${!authors[#]}"; do
echo "${authors["$k"]} ${k}"
done | sort -rn | while read number author; do
echo "${author} - ${number}"
done
Example:
$ declare -A authors
$ authors=(['Shakespeare']=1 ['Kant']=2 ['Von Neumann']=3 ['Von Auersperg']=4)
$ for k in "${!authors[#]}"; do echo "${authors["$k"]} ${k}"; done | sort -rn | while read number author; do echo "${author} - ${number}"; done
Von Auersperg - 4
Von Neumann - 3
Kant - 2
Shakespeare - 1
$
The chosen answer seems to work if there are no spaces in the keys, but fails if there are:
$ declare -A authors
$ authors=(['Shakespeare']=1 ['Kant']=2 ['Von Neumann']=3 ['Von Auersperg']=4)
$ for k in "${!authors[#]}"; do echo $k ' - ' ${authors["$k"]}; done | sort -rn -k 3
Kant - 2
Shakespeare - 1
Von Neumann - 3
Von Auersperg - 4
$

Resources