shell script - is there any way converting number to char? [duplicate] - bash

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Integer ASCII value to character in BASH using printf
I want to convert my integer number to ASCII character
We can convert in java like this:
int i = 97; //97 is "a" in ASCII
char c = (char) i; //c is now "a"
But,is there any way in to do this shell scripting?

#!/bin/bash
# chr() - converts decimal value to its ASCII character representation
# ord() - converts ASCII character to its decimal value
chr() {
printf \\$(printf '%03o' $1)
}
ord() {
printf '%d' "'$1"
}
ord A
echo
chr 65
echo
Edit:
As you see ord() is a little tricky -- putting a single quote in front of an integer.
The Single Unix Specification: "If the leading character is a
single-quote or double-quote, the value shall be the numeric value in
the underlying codeset of the character following the single-quote or
double-quote."
(Taken from http://mywiki.wooledge.org/BashFAQ/071).
See man printf(1p).

declare -i i=97
c=$(printf \\$(printf '%03o' $i))
echo "char:" $c

Related

Format currency in Bash

Is it possible to format currency in Bash?
Example data is received as
19366
Data to be displayed as
$193,66
Thanks.
Simply handle your value as a text string, instead of a number, and insert a dollar sign and a comma at the correct positions:
$ v=19366
$ printf '$%s,%s\n' "${v:0: -2}" "${v: -2}"
$193,66
${v:offset:length) expands as the substring of $v that starts at character offset (counting from 0) and which length is length. But negative offsets and lengths can be used to refer to the end of the string.
${v:0:-2} expands as the substring of $v that starts at the beginning (0) and which length is the number of remaining characters minus two (-2). In our example this is 193.
${v: -2} expands as the substring of $v that starts two characters before the end (-2) and which length (not specified) is the number of remaining characters. In our example this is 66. Note the space between : and -2, it is needed to avoid another interpretation by the shell (providing default value 2 if v is unset or null).
Preamble In your request, you use coma , as decimal separator (radix mark). For locale support, see second part of my answer.
1. Pseudo floating poing using integer as strings
I often use this kind of pseudo float:
amount=123456
amount=00$amount # avoid bad length error
printf '$%.2f\n' ${amount::-2}.${amount: -2}
$1234.56
for amount in 0 1 12 123 1234 12345;do
amount=00$amount
printf '$%.2f\n' ${amount::-2}.${amount: -2}
done
$0.00
$0.01
$0.12
$1.23
$12.34
$123.45
As a function:
int2amount() {
if [[ $1 == -v ]]; then
local -n _out="$2"
shift 2
else
local _out
fi
local _amount=00$(($1))
printf -v _out $'$%\47.2f' ${_amount::-2}.${_amount: -2}
[[ ${_out#A} != _out=* ]] || echo "$_out"
}
Then
int2amount 123456
$1’234.56
int2amount -v var 1234567
echo $var
$12’345.67
2. Remark regarding locale, decimal separator and thousand separators
In your request, your radix mark is a coma ,. This depend on your locale configuration. U could hit something like:
set | grep ^LC\\\|^LANG
to show how this is configured on your host.
As there are many issues regarding locales, I've asked How to determine which character is used as decimal separator or thousand separator under current locale as separated question.
Try:
for locvar in C en_US.UTF-8 de_DE.UTF-8 ;do
LC_NUMERIC=$locvar int2amount 1234567
done
$12345.67
$12,345.67
bash: line 1: printf: 0012345.67: invalid octal number
$12.345,00
Error because unsing de_DE locale configuration, you have to use a coma as separator (Decimal separator at wikipedia).
This is already know to produce issues using bc: How do I change the decimal separator in the printf command in bash?
Final function unsing variable decimal separator
int2amount () {
if [[ $1 == -v ]]; then
local -n _out="$2"
shift 2
else
local _out
fi
local _amount=00$(($1)) _decsep
printf -v _decsep %.1f 1
_decsep=${_decsep:1:1}
printf -v _out '$%'\''.2f' ${_amount::-2}${_decsep}${_amount: -2}
[[ ${_out#A} != _out=* ]] || echo "$_out"
}
for locvar in C en_US.UTF-8 de_DE.UTF-8 ;do
LC_NUMERIC=$locvar int2amount 1234567
done
$12345.67
$12,345.67
$12.345,67
Note about LC_ALL: If in your environment, a variable $LC_ALL is defined, all demos using LC_NUMERIC won't work because LC_ALL is over. You have to unset LC_ALL or use:
LC_ALL=$locvar LC_NUMERIC=$locvar int2amount 1234567
in last demo.
You can use printf
amount="240570.578"
printf "%'.2f\n" $amount
> 240,570.58
printf does have a thousands grouping format specifier flag, however the character used to denote the groups (non-monetary grouping character) depends on locale (LC_NUMERIC).
The C or POSIX locale uses no grouping character. Therefore you can't do this portably with printf.
printf "%'d\n" 19366
Works if the current locale supports the comma grouping character.
In my bashrc, I use the following function to add thousands groupings to any integer, using comma (,) and preserving a non numeric prefix (like $, or - for negative numbers). It doesn't depend on locale, but does require rev.
commafy ()
{
printf %s "${1%%[0-9]*}"
printf '%s\n' "${1##*[!0-9]}" |
rev |
sed -E 's/[0-9]{3}/&,/g; s/,$//' |
rev
}
Example:
commafy '$19366'
# gives
$19,366
You could slightly simplify this too:
printf %s \$
printf '%s\n' 19366 |
rev |
sed -E 's/[0-9]{3}/&,/g; s/,$//' |
rev
Simplistically -
$: sed -E 's/([0-9]*)([0-9][0-9])$/$\1,\2/'<<<"19366"
$193,66

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"

Shift between two characters

How to get a shift between two characters in bash?
For instance, in C++ we have:
'c'-'a'=2
Are there any elegant solutions?
Define ord to get the ASCII value of each character (from Unix & Linux Stack Exchange, Bash FAQ):
ord() { LC_CTYPE=C printf '%d' "'$1"; }
(note that the ' is not a typo! It is required for printf to treat a character as a number1)
Then you can subtract one from the other:
$ echo "$(( "$(ord c)" - "$(ord a)" ))"
2
If you wanted to put this in a function, you could:
diff_ord() { echo "$(( "$(ord $1)" - "$(ord $2)" ))"; }
Then call it like:
$ diff_ord c a
2
If the leading character is a single-quote or double-quote, the value shall be the numeric value in the underlying codeset of the character following the single-quote or double-quote.

How to update property file values from their character value to its equivalent ASCII value

I have a property file (test.properties) with below contents. The value has only one alphabet
key1=D
k1ey=A
key3=B
I want to update all the property values with their ASCII values.
key1=068
k1ey=065
key3=066
How can I update the property values as shown above and save it to new file (final.properties) in a bash script
Using this BashFAQ-071 How do I convert an ASCII character to its decimal (or hexadecimal) value and back? as reference, you can do a neat little trick as shown below to convert ASCII character to its decimal value.
#!/usr/bin/env bash
while IFS== read -r key value; do
LC_CTYPE=C printf -v decimalValue %03d "'$value"
printf '%s=%s\n' "$key" "$decimalValue"
done < test.properties > final.properties
The idea is we pre-pend the property values with a ' and then printf in-turn converts it to is ASCII equivalent of the value and then we print it out with a format specifier %03d to represent it as decimal value.
If you can use perl it's super simple
perl -pe 's/=\K./ord$&/e' test.properties > final.properties

How to loop through a range of characters in a bash script using ASCII values?

I am trying to write a bash script which will read two letter variables (startletter/stopletter) and after that I need to print from the start letter to the stop letter with a for or something else. How can I do that?
I tried to do
#! /bin/bash
echo "give start letter"
read start
echo "give stop letter" read stop
But none of the for constructs work
#for value in {a..z}
#for value in {$start..$stop}
#for (( i=$start; i<=$stop; i++)) do echo "Letter: $c" done
This question is very well explained in BashFAQ/071 How do I convert an ASCII character to its decimal (or hexadecimal) value and back?
# POSIX
# chr() - converts decimal value to its ASCII character representation
# ord() - converts ASCII character to its decimal value
chr () {
local val
[ "$1" -lt 256 ] || return 1
printf -v val %o "$1"; printf "\\$val "
# That one requires bash 3.1 or above.
}
ord() {
# POSIX
LC_CTYPE=C printf %d "'$1"
}
Re-using them for your requirement, a proper script would be written as
read -p "Input two variables: " startLetter stopLetter
[[ -z "$startLetter" || -z "$stopLetter" ]] && { printf 'one of the inputs is empty\n' >&2 ; }
asciiStart=$(ord "$startLetter")
asciiStop=$(ord "$stopLetter")
for ((i=asciiStart; i<=asciiStop; i++)); do
chr "$i"
done
Would print the letters as expected.
Adding it to community-wiki since this is also a cross-site duplicate from Unix.SE - Bash script to get ASCII values for alphabet
In case you feel adventurous and want to use zsh instead of bash, you can use the following:
For zsh versions below 5.0.7 you can use the BRACE_CCL option:
(snip man zshall) If a brace expression matches none of the above forms, it is left
unchanged, unless the option BRACE_CCL (an abbreviation for 'brace character class') is set. In that case, it is expanded to a list of the individual characters between the braces sorted into the order of the characters in the ASCII character set (multibyte characters are not currently handled). The syntax is similar to a [...] expression in filename generation: - is treated specially to denote a range of characters, but ^ or ! as the first character is treated normally. For example, {abcdef0-9}
expands to 16 words 0 1 2 3 4 5 6 7 8 9 a b c d e f.
#!/usr/bin/env zsh
setopt brace_ccl
echo "give start letter"
read cstart
echo "give stop letter"
read cstop
for char in {${cstart}-${cstop}}; do echo $char; done
For zsh versions from 5.0.7 onwards you can use the default brace expansion :
An expression of the form {c1..c2}, where c1 and c2 are single characters (which may be multibyte characters), is expanded to every character in the range from c1 to c2 in whatever character sequence is used internally. For characters with code points below 128 this is US ASCII (this is the only case most users will need). If any intervening character is not printable, appropriate quotation is used to render it printable. If the character sequence is reversed, the output is in reverse order, e.g. {d..a} is substituted as d c b a.
#!/usr/bin/env zsh
echo "give start letter"
read cstart
echo "give stop letter"
read cstop
for char in {${cstart}..${cend}; do echo $char; done
More information on zsh can be found here and the quick reference

Resources