Convert (integer,precision) pair to a float in bash/bc - bash

I have two numbers, $value and $precision. I need to make a "floating-point" representation of these numbers (though of course the output will actually be a string, since this is bash).
$precision tells me how many decimal points the number should have.
Example:
$value=123, $precision=2
This should give the output "1.23".
How can I do this elegantly from Bash? I am looking at the "bc" man page but I'm not really seeing any way there. I assume the simplest way is to treat my input value as a string and insert the char '.' in the right position somehow.

In bc:
bc <<EOF
scale = $precision
$value / (10 ^ $precision)
EOF
In sed:
sed -e "s/[0-9]\{$precision\}$/.&/" <<< "$value"

OK, so I found some string manipulation help for Bash ... This does the trick, but it is hardly pretty. Posting it here for posterity.
lastIndex=$((${#value}-1))
dotIndex=$((${#value}-$precision))
decvalue=${value:0:$dotIndex}.${value:$dotIndex:$lastIndex}

Related

How do you convert characters to ASCII without use of the printf in bash

ascii() {printf '%d' "'$1"}
I am currently using this function to convert characters to ASCII, however I just want to store the result of the function as a variable without printing the ascii. How would I go about this? (please bear in mind I have only been using bash for a few hours total, so sorry if this is a dumb question.)
In bash, after
printf -v numval "%d" "'$1"
the variable numval (you can use any other valid variable name) will hold the numerical value of the first character of the string contained in the positional parameter $1.
Alternatively, you can use the command substitution:
numval=$(printf "%d" "'$1")
Note that these still use printf but won't print anything to stdout.
As stated in the comment by #Charles Duffy, the printf -v version is more efficient, but less portable (standard POSIX shell does not support the -v option).
Thx for your script! I didn't know how get ascii values so "'M" rescued me.
I pass parameters to function to get return.
Function returns I use to... Well, return err/status codes.
#!/bin/sh
# posix
ascii () {
# $1 decimal ascii code return
# $2 character
eval $1=$(printf '%d' "'$2")
}
ascii cod 'M'
echo "'M' = $cod"

How to convert a semantic version shell variable to a shifted integer?

Given a shell variable whose value is a semantic version, how can I create another shell variable whose value is (tuple 1 × 1000000) + (tuple 2 × 1000) + (tuple 3) ?
E.g.
$ FOO=1.2.3
$ BAR=#shell magic that, given ${FOO} returns `1002003`
# Shell-native string-manipulation? sed? ...?
I'm unclear about how POSIX-compliance vs. shell-specific syntax comes into play here, but I think a solution not bash-specific is preferred.
Update: To clarify: this isn't as straightforward as replacing "." with zero(es), which was my initial thought.
E.g. The desired output for 1.12.30 is 1012030, not 100120030, which is what a .-replacement approach might provide.
Bonus if the answer can be a one-liner variable-assignment.
A perl one-liner:
echo $FOO | perl -pne 's/\.(\d+)/sprintf "%03d", $1/eg'
How it works:
perl -pne does a REPL with the supplied program
The program contains a replacement function s///
The search string is the regex \.(\d+) which matches a string beginning with dot and ends with digits and capture those digits
The e modifier of the s/// function evaluates the right-hand side of the s/// replacement as an expression. Since we captured the digits, they'll be converted into int and formatted into leading zeros with sprintf
The g modifier replaces all instances of the regex in the input string
Demo
Split on dots, then loop and multiply/add:
version="1.12.30"
# Split on dots instead of spaces from now on
IFS="."
# Loop over each number and accumulate
int=0
for n in $version
do
int=$((int*1000 + n))
done
echo "$version is $int"
Be aware that this treats 1.2 and 0.1.2 the same. If you want to always treat the first number as major/million, consider padding/truncating beforehand.
This should do it
echo $foo | sed 's/\./00/g'
How about this?
$ ver=1.12.30
$ foo=$(bar=($(echo $ver|sed 's/\./ /g')); expr ${bar[0]} \* 1000000 + ${bar[1]} \* 1000 + ${bar[2]})
$ echo $foo
1012030

How do I recursively replace part of a string with another given string in bash?

I need to write bash script that converts a string of only integers "intString" to :id. intString always exists after /, may never contain any other types (create_step2 is not a valid intString), and may end at either a second / or end of line. intString may be any 1-8 characters. Script needs to be repeated for every line in a given file.
For example:
/sample/123456/url should be converted to /sample/:id/url
and /sample_url/9 should be converted to /sampleurl/:id however /sample_url_2/ should remain the same.
Any help would be appreciated!
It seems like the long way around the problem to go recursive but then I don't know what problem you are solving. It seems like a good sed command like
sed -E 's/\/[0-9]{1,}/\/:id/g'
could do it in one shot, but if you insist on being recursive then it might go something like this ...
#!/bin/bash
function restring()
{
s="$1"
s="$(echo $s | sed -E 's/\/[0-9]{1,}/\/:id/')"
if ( echo $s | grep -E '\/[0-9]{1,}' > /dev/null ) ; then
restring $s
else
echo $s
exit
fi
echo $s
}
restring "$1"
now run it
$ ./restring.sh "/foo/123/bar/456/baz/45435/andstuff"
/foo/:id/bar/:id/baz/:id/andstuff

atoi() like function in bash?

Imagine that I use a state file to store a number, I read the number like this:
COUNT=$(< /tmp/state_file)
But since the file could be disrupted, $COUNT may not contain a "number", but any characters.
Other than using regex, i.e if [[ $COUNT ~ ^[0-9]+$ ]]; then blabla; fi, is there a "atoi" function that convert it to a number(0 if invalid)?
EDIT
Finally I decided to use something like this:
let a=$(($a+0))
Or
declare -i a; a="abcd123"; echo $a # got 0
Thanks to J20 for the hint.
You don't need an atoi equivalent, Bash variables are untyped. Trying to use variables set to random characters in arithmetic will just silently ignore them. eg
foo1=1
foo2=bar
let foo3=foo1+foo2
echo $foo3
Gives the result 1.
See this reference
echo $COUNT | bc should be able to cast a number, prone to error as per jurgemaister's comments...
echo ${COUNT/[a-Z]*} | bc which is similar to your regex method but not prone to error.
case "$c" in
[0-9])...
You should eat the input string charwise.

how to chop last n bytes of a string in bash string choping?

for example qa_sharutils-2009-04-22-15-20-39, want chop last 20 bytes, and get 'qa_sharutils'.
I know how to do it in sed, but why $A=${A/.\{20\}$/} does not work?
Thanks!
If your string is stored in a variable called $str, then this will get you give you the substring without the last 20 digits in bash
${str:0:${#str} - 20}
basically, string slicing can be done using
${[variableName]:[startIndex]:[length]}
and the length of a string is
${#[variableName]}
EDIT:
solution using sed that works on files:
sed 's/.\{20\}$//' < inputFile
similar to substr('abcdefg', 2-1, 3) in php:
echo 'abcdefg'|tail -c +2|head -c 3
using awk:
echo $str | awk '{print substr($0,1,length($0)-20)}'
or using strings manipulation - echo ${string:position:length}:
echo ${str:0:$((${#str}-20))}
In the ${parameter/pattern/string} syntax in bash, pattern is a path wildcard-style pattern, not a regular expression. In wildcard syntax a dot . is just a literal dot and curly braces are used to match a choice of options (like the pipe | in regular expressions), so that line will simply erase the literal string ".20".
There are several ways to accomplish the basic task.
$ str="qa_sharutils-2009-04-22-15-20-39"
If you want to strip the last 20 characters. This substring selection is zero based:
$ echo ${str::${#str}-20}
qa_sharutils
The "%" and "%%" to strip from the right hand side of the string. For instance, if you want the basename, minus anything that follows the first "-":
$ echo ${str%%-*}
qa_sharutils
only if your last 20 bytes is always date.
$ str="qa_sharutils-2009-04-22-15-20-39"
$ IFS="-"
$ set -- $str
$ echo $1
qa_sharutils
$ unset IFS
or when first dash and beyond are not needed.
$ echo ${str%%-*}
qa_sharutils

Resources