Extract one number from string in bash - bash

I have this string:
1024.00 MB transferred (912.48 MB/sec)
and I need to get only the number 912.48 and transform it in 912,48 with a bash script.
I tried to do sed 's/[^0-9.]*//g' but in this way i get 1024.00 912.18.
How can I do it?

So far, every answer here is using external tools (sed, awk, grep, tr, etc) rather than sticking to native bash functionality. Since spinning up external processes has a significant constant-time performance impact, it's generally undesirable when only processing a single line of content (for long streams of content, an external tool will often be more efficient).
This one uses built-ins only:
# one-time setup: set the regex
re='[(]([0-9.]+) MB/sec[)]'
string='1024.00 MB transferred (912.48 MB/sec)'
if [[ $string =~ $re ]]; then # run enclosed code only if regex matches
val=${BASH_REMATCH[1]} # refer to first (and only) match group
val_with_comma=${val//./,} # replace "." with "," in that group
echo "${val_with_comma}" # ...and emit our output
fi
...yielding:
912,48

A combination of awk and sed:
str='1024.00 MB transferred (912.48 MB/sec)'
echo "$str" | awk '{print $4}' | sed 's/(//;s/\./,/'
912,48
Or entirely with awk:
echo "$str" | awk '{sub("[(]","");sub("[.]",",");print $4}'

this should work
$ sed -r 's/.*\(([0-9.]+).*/\1/;s/\./,/'

echo "1024.00 MB transferred (912.48 MB/sec)" | cut -f2 -d'(' | cut -f1 -d' ' | sed 's/\./,/'

echo "1024.00 MB transferred (912.48 MB/sec)" | cut -d " " -f4 | tr "." "," | tr -d "("

Another of the near-infinite possibilities:
read x y < <(tr -dc '[0-9. ]' <<< "1024.00 MB transferred (912.48 MB/sec)")
echo ${y}
or
grep -oP '(?<=\()[\d.]+' <<< "1024.00 MB transferred (912.48 MB/sec)"

Here is an awk to get the job done:
s='1024.00 MB transferred (912.48 MB/sec)'
awk -F '[() ]+' '{sub(/\./, ",", $4); print $4}' <<< "$s"
912,48

wow, so many answers :)
here's mine, should be pretty fast:
grep -o '([^ ]\+' | tail -c+2 | tr '.' ','

Related

Shell Script Showing syntax error for dollar $

#!/bin/bash
LIMIT='50'
DIR="( $(df -Ph | column -t | awk '{print $5}' | grep -v Use) )"
for i in $DIR;
do
USED=$(df -Ph $i | awk '{print $5}' | sed -ne 2p | cut -d"%" -f1)
if [ "$USED" -gt "$LIMIT" ];
#If used space is bigger than LIMIT
then
#####
fi
done
Why am I getting syntax error at line 5 ? in for loop for variable $DIR?
I think the fundamental error is the quotes in the assignment of the array. Instead of DIR="( $(...) )", you need to drop the quotes and use DIR=( $(...) ). However, that assignment isn't necessary at all!
You probably shouldn't parse df like this, but you definitely should not be running df multiple time. There's no need for the embedded loop. Since you haven't really should what you're doing in the cases when the filesystem use is over the limit, it's hard to give better code, but whatever you're doing there can almost certainly be done easily in awk. Or, if not in awk, you can use awk rather than the embedded loop to trigger the action. eg, you could just do:
df -Ph | awk 'NR>1{printf("%s: %s than limit\n", $1, $5 + 0> limit ? "bigger" : "smaller")}' limit="${LIMIT-50}"
altough it probably makes more sense to do:
df -Ph | awk 'NR>1 && $5 + 0 > limit {print $1 " is over limit" > "/dev/stderr"}' limit="${LIMIT-50}"
note that both of these fail horribly if any of the column of the output of df contain whitespace (eg "map auto_home"). The output of df is intended for human consumption, and is not really suited to this sort of thing. You could do a column count in the awk (or use $(NF-1) instead of $5) and get the Capacity that way, but that's just moving the fragility.

Get avg of cpu temp's as a one line script

I need to average the temperatures of the four cpu cores on my system. I am obtaining the temperatures of the individual cores using the command:
sysctl -a | awk '/temperature/ {print $2;}'
This spits out the following output:
53.0C
53.0C
52.0C
52.0C
I then pass this to sed and tr and with some script-fu I ended up with the following-one liner:
echo `sysctl -a | awk '/temperature/ {print $2;}' | sed s/C// | tr '\n' '+' | sed 's/\(.*\)+/\1/'` | bc`
which then results in:
210
I now simply need to divide 210/4 to get my average but am stumped on how to achieve this as an extension to the one-liner that I have already brewed up. And due to some other constraints, I need to keep this as a one-liner.
I am sure there's a simpler way to achieve what I am after, any pointers are appreciated!
With awk:
sysctl -a | awk -F '[ C]' '/temperature/{sum+=$2} END{print sum/NF}'
Output:
52.5
See: 8 Powerful Awk Built-in Variables – FS, OFS, RS, ORS, NR, NF, FILENAME, FNR
You can add parenthesis and division by 4 around your expression with:
{ echo -n "("; tr '\n' '+'; echo -n")/4"; }
The final result is:
echo `sysctl -a | awk '/temperature/ {print $2;}' | sed s/C// | { echo -n "("; tr '\n' '+'; echo -n")/4"; } | sed 's/\(.*\)+/\1/'` | bc`

Bash math - Dividing a bunch of rows using for statement

Example file:
25 Firstname1 Lastname1 domain1.com planname #1.00 USD Monthly Active 04/24/2016 Edit
1068 Firstname2 Lastname2 domain2.com planname #7.95 USD Annually Active 05/09/2016 Edit
3888 Firstname3 Lastname3 domain3.com planname #19.95 USD Biennially Active 05/04/2016 Edit
I am extracting just the price and billing cycle and am converting the billing cycles into numerical value this way I can divide the price by the billing cycle to get a cost per month.
When using the for statement, its adding line breaks which is breaking the math.
Code:
for i in `cat asd | cut -d "#" -f 2 | awk '{print $1, $3}' | sed 's/Monthly/\/ 1/g' | sed 's/Annually/\/ 12/g' | sed 's/Biennially/\/ 24/g' |grep -Ev 0.00` ; do echo $i | bc -l' ; done
I would prefer to be able to get 1 answer meaning all the rows get divided up then added together to get one final answer.
All those calls to cat, cut, awk, sed, grep and bc - what a waste.
This is a mis-named post, because you are not using Bash to do any calculations. The reason is that bash, unlike korn shell (ksh), does not support floating point. So you fall back to utilities like bc. Hold on though, awk supports floating point as well.
awk is a programming language in its own right. This just uses one instance of awk. I have embedded it inside a bash script because you are probably doing other stuff, but with a little adjustment it could be stand-alone with #!/bin/awk at the top:
infile='asd'
# -f - means "read the program from stdin"
# << '_END_' is a here document. Redirect stdin from here to the label _END_
awk -f - "$infile" << '_END_'
BEGIN {
# an associative array for the billing cycles
cycles["Monthly"] = 1
cycles["Annually"] = 12
cycles["Biennially"] = 24
}
{
sub(/#/,"",$6) # Remove the # from the amount
total += $6/cycles[$8] # divide amount by the billing cycle, add to total
}
END { print total }
_END_
Don't you think this is simpler to understand and maintain? It's also more efficient. This awk script is probably a good exercise for an awk 101 training course.
You could do something like this: (If you are totally set on a single line)
cat asd | cut -d "#" -f 2 | awk '{print $1, $3}' | sed 's/Monthly/\/ 1/g' | sed 's/Annually/\/ 12/g' | sed 's/Biennially/\/ 24/g' | grep -Ev 0.00 | while IFS= read -r line; do echo "$line" | bc -l; done | tr '\n' '+' | sed 's/+$/\n/' | bc -l
But this would be way more clear:
tmp=$(mktemp)
cat asd | cut -d "#" -f 2 | awk '{print $1, $3}' | sed 's/Monthly/\/ 1/g' | sed 's/Annually/\/ 12/g' | sed 's/Biennially/\/ 24/g' | grep -Ev 0.00 > $tmp
tmp2=$(mktemp)
cat $tmp | while IFS= read -r line; do
echo "$line" | bc -l >> $tmp2
done
# Actual output
cat $tmp2 | tr '\n' '+' | sed 's/+$/\n/' | bc -l
rm $tmp $tmp2

sort fields within a line

input:
87 6,1,9,13
3 9,4,14,35,38,13
31 3,1,6,5
(i.e. a tab-delimited column where the second field is a comma-delimited list of unordered integers.)
desired output:
87 1,6,9,13
3 4,9,13,14,35,38
31 1,3,5,6
Goal:
for each line separately, sort the comma-separated list appearing in the second field. i.e. sort the 2nd column within for each line separately.
Note: the rows should not be re-ordered.
What I've tried:
sort - Since the order of the rows should not change, then sort is simply not applicable.
awk - since the greater file is tab-delimited, not comma-delimited, it cannot parse the second column as multiple "sub-fields"
There might be a perl way? I know nothing about perl though...
It can be done by simple perl oneliner:
perl -F'/\t/' -alne'$s=join",",sort{$a<=>$b}split",",$F[1];print"$F[0]\t$s"'
and shell (bash) one as well:
while read a b;do echo -e "$a\t$(echo $b|tr , '\n'|sort -n|tr '\n' ,|sed 's/,$//')"; done
while read LINE; do
echo -e "$(echo $LINE | awk '{print $1}')\t$(echo $LINE | awk '{print $2}' | tr ',' '\n' | sort -n | paste -s -d,)";
done < input
Obviously a lot going on here so here we go:
input contains your input
$(echo $LINE | awk '{print $1}') prints the first field, pretty straightforward
$(echo $LINE | awk '{print $2}' | tr ',' '\n' | sort -n | paste -s -d,) prints the second field, but breaks it down into lines by replacing the commas by newlines (tr ',' '\n'), then sort numerically, then assemble the lines back to comma-delimited values (paste -s -d,).
$ cat input
87 6,1,9,13
3 9,4,14,35,38,13
31 3,1,6,5
$ while read LINE; do echo -e "$(echo $LINE | awk '{print $1}')\t$(echo $LINE | awk '{print $2}' | tr ',' '\n' | sort -n | paste -s -d,)"; done < input
87 1,6,9,13
3 4,9,13,14,35,38
31 1,3,5,6
Another way:
echo happybirthday|awk '{split($0,A);asort(A); for (i=1;i<length(A);i++) {print A[i]}}' FS=""|tr -d '\n';echo aabdhhipprty
I didn't know how to get back to this page after recovering login info, so am posting as a guest.

Extract text from hostname

Using OS X, I need a one line bash script to look at a client mac hostname like:
12345-BA-PreSchool-LT.local
Where the first 5 digits are an asset serial number, and the hyphens separate a business unit code from a department name followed by something like 'LT' to denote a laptop.
I guess I need to echo the hostname and use a combination of sed, awk and perhaps cut to strip characters out to leave me with:
"BA PreSchool"
Any help much appreciated. This is what I have so far:
echo $HOSTNAME | sed 's/...\(...\)//' | sed 's/.local//'
echo "12345-BA-PreSchool-LT.local" | cut -d'-' -f2,3 | sed -e 's/-/ /g'
(Not on OSX, so not sure if cut is defined)
I like to keep things simple :)
You could do it with just cut:
echo 12345-BA-PreSchool-LT.local | cut -d"-" -f2,3
BA-PreSchool
If you want to remove the hyphen you can use tr
echo 12345-BA-PreSchool-LT.local | cut -d"-" -f2,3 | tr "-" " "
BA PreSchool
How about
echo $HOSTNAME | awk 'BEGIN { FS = "-" } ; { print $2, $3 }'
Awk can solve your question easily.
echo "12345-BA-PreSchool-LT.local" | awk -F'-' '$0=$2" "$3'
BA PreSchool
bash$ string="12345-BA-PreSchool-LT.local"
bash$ IFS="-"
bash$ set -- $string
bash$ echo $2-$3
BA-PreSchool

Resources