How can I print 55 as hexadecimal with echo from decimal number?
#equivalent to:
printf "%x" 55
#like this:
dec=55
hex=$(([##16]dec))
echo $[0xAA]
I searched a lot but did not find any echo snippets for the calculation from decimal to hexadecimal.
Try this:
#!/bin/sh
OUTPUT=`echo $1 |dc -e "16o?p"`
echo $OUTPUT
and i have:
user#raspberrypi:~ $ ./convert.sh 999
3E7
How can I print 55 as hexadecimal with echo from decimal number?
It is not possible, echo as the name implies, echos what you give to it, it does no conversion.
like this:
Yes, you can write a utility to convert a decimal number into hexadecimal number in shell. There is no such builtin conversion.
Converting a number to hex, or any other base, is relatively easy. For example in Bash:
hex() {
local out="" dict=($(seq 0 9) a b c e d f)
while (($1)); do
out=${dict[$1 % 16]}$out
set -- $(($1 / 16))
done
echo $out
}
hex 55
Conceptually same code, in sh:
hex () {
out=""
while [ $1 -ne 0 ]; do
c=$(($1 % 16))
case $c in
10) c=a ;; 11) c=b ;; 12) c=c ;;
13) c=d ;; 14) c=e ;; 15) c=f ;;
esac
out=$c$out
set -- $(($1 / 16))
done
echo $out
}
hex 55
Related
I have a single byte, 0x1, inside t.data.
How would I read that file to perform bitwise math on it's contents in POSIX shell?
echo $((1 << 1))
gives 2, but
echo $(($((cat t.data)) << 1))
and var d=$(< t.data); echo $(("$d" << 1))
fail.
POSIX sh and Bash are not suitable for processing binary data, but you can use printf to convert back and forth between bytes and integers:
# Read the ascii value of the first byte
num=$(printf "%d" "'$(head -c 1 < t.data)")
echo "The decimal representation of the first byte is $num"
# Do some math on it
num=$(( (num << 1) & 0xFF ))
echo "After shifting by one, it became $num"
# Write back the result via an octal escape
oct=$(printf '%03o' "$num")
printf "\\$oct" > t.data
I need to write the 4 bytes representation of a number (176) into a .bin file. I was using the following command
perl -we 'print pack "N", shift' 176 >> package.bin
and it works fine. But now I'd need to do exactly the same thing without using perl. Is it possible? I can only find solutions with perl but unfortunatly I can't use it because of project requirements.
I also tried this solution:
local n bit
for (( n=$1 ; n>0 ; n >>= 1 )); do bit="$(( n&1 ))$bit"; done
echo -n -e $bit > tobin.bin
But it doesn't work because it writes 10110000 into my destination file and it is wrong because it is considered to be 8 bytes long and not 4 (1 byte for each character).
Try
num=176
printf -v oct0 '%03o' "$(( (num>>24) & 0xff ))"
printf -v oct1 '%03o' "$(( (num>>16) & 0xff ))"
printf -v oct2 '%03o' "$(( (num>> 8) & 0xff ))"
printf -v oct3 '%03o' "$(( num & 0xff ))"
printf "\\$oct0\\$oct1\\$oct2\\$oct3" >package.bin
You can set num to any 32-bit positive integer value.
See BashFAQ/071 (How do I convert an ASCII character to its decimal (or hexadecimal) value and back?) for details of how the conversion is done.
At the very beginning of my Bash script I'm passing a set of characters like this:
#!/bin/bash
set0="0 1 2 3 4 5 6 7 8 9"
set1="° § + \" ç % & / ( ) = ? ' ^ ! £ $ - _ ; , : . *"
The script combines every single character with the others in order to get all possible combination with length of n.
As you can see in set1 above I have to escape the quote " character for example. My problem now is, that I have to escape the asterisk * as well. But I couldn't figure out how.
This didn't work so far:
#!/bin/bash
set1="° § + \" ç % & / ( ) = ? ' ^ ! £ $ - _ ; , : . \*"
It echoes \*. If I don't escape the asterisk * with a backslash \ the script echoes all the files in current directory all time.
Do you know how I can escape the asterisk * successfully?
This is the whole script:
8 chars0="a b c d e f g h i j k l m n o p q r s t u v w x y z"
9 chars1="A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
10 chars2="0 1 2 3 4 5 6 7 8 9"
11 chars3="° § + \" ç % & / ( ) = ? ' ^ ! £ $ - _ ; , : . *"
12
13 function increment {
14 INPUT=$1
15 [[ ${#INPUT} -ge $minlen ]] && echo $1
16 if [[ ${#INPUT} -lt $maxlen ]]; then
17 for c in $chars; do
18 increment $1$c
19 done
20 fi
21 }
22
23 function showUsage {
24 echo "$0 MAXLEN [MINLEN] [a [A [n [p]]]]"
25 echo
26 echo " MAXLEN integer Maximum lenght, or specific lengt if MINLEN is omitted"
27 echo " MINLEN integer Minimum lenght, equals MAXLEN if omitted"
28 echo ""
29 echo " characters:"
30 echo " a lower case a-z"
31 echo " A Uppercase A-Z"
32 echo " n numeric 0-9"
33 echo " p punctuation . ?"
34 }
35
36
37
38
39 ## argument handler
40 [[ "$1" = "" ]] && showUsage && exit
41 maxlen=$1
42 [[ $2 -eq 0 ]] && minlen=$maxlen || minlen=$2
43
44
45 for arg in "$#"; do
46 case $arg in
47 a) chars="$chars $chars0" ;;
48 A) chars="$chars $chars1" ;;
49 n) chars="$chars $chars2" ;;
50 p) chars="$chars $chars3" ;;
51 esac;
52 done
53
54 #fallback
55 [[ "$chars" = "" ]] && chars="$chars0"
56
57
58
59 ## kickof
60
61 for i in $chars; do
62 increment $i
63 done
Thank you for all your suggestions. The one that helped me fixing this was
set -f
It's a quick fix of course. But it works well for me.
The best way to do this is maybe to use:
set -o noglob
set="* + ?"
for i in $set; do echo $i; done
*
+
?
The problem when you use the variable. noglob will prevent all of the file glob characters from expanding.
You can't safely loop on a list of words in a string like this, without having nasty globbing effects. In your case, it boils down to this:
a=*
for i in $a; do
echo "$a"
done
You'll see that the files in your current directory are printed, because the (unquoted! horror!) $a in the for statement expands to * which will glob to the list of files in current dir.
You must fix this by using another design. You're lucky, Bash supports arrays very well, so redesign your code to use arrays. For example:
chars=( ° § + \" ç % \& / '(' ')' = \? \' ^ ! £ $ - _ \; , : . \* )
(this defines the array chars) and to loop through that:
for i in "${chars[#]}"; do
echo "$i"
done
(observe the quotes).
Below is a rewriting of your code to show you how to use arrays (and I've fixed/improved some other minor stuff too):
#!/bin/bash
chars_alpha=( {a..z} )
chars_ALPHA=( {A..Z} )
chars_digit=( {0..9} )
chars_punct=( '°' '§' '+' '"' 'ç' '%' '&' '/' '(' ')' '=' '?' "'" '^' '!' '£' '$' '-' '_' ';' ',' ':' '.' '*' )
increment() {
local input=$1
(( ${#input} >= minlen )) && printf '%s\n' "$input"
if (( ${#input} < maxlen )); then
for c in "${chars[#]}"; do
increment "$input$c"
done
fi
}
showUsage() {
cat <<EOF
$0 MAXLEN [MINLEN] [a] [A] [n] [p]
MAXLEN integer Maximum lenght, or specific lengt if MINLEN is omitted
MINLEN integer Minimum lenght, equals MAXLEN if omitted
characters:
a lower case a-z
A Uppercase A-Z
n numeric 0-9
p punctuation . ? etc.
EOF
}
## argument handler
(($#)) || { showUsage; exit 1; }
[[ $1 = +([[:digit:]]) ]] || { showUsage; exit 1; }
((maxlen=10#$1))
shift
# Check that maxlen is >0 or exit gracefully
((maxlen>0)) || exit 0
if [[ $1 = +([[:digit:]]) ]]; then
((minlen=10#$1))
shift
else
((minlen=maxlen))
fi
# Check that minlen<=maxlen otherwise swap them
if ((minlen>maxlen)); then
((temp=minlen,minlen=maxlen,maxlen=temp))
fi
chars=()
while (($#)); do
case $1 in
(a) chars+=( "${chars_alpha[#]}" ); chars_alpha=() ;;
(A) chars+=( "${chars_ALPHA[#]}" ); chars_ALPHA=() ;;
(n) chars+=( "${chars_digit[#]}" ); chars_digit=() ;;
(p) chars+=( "${chars_punct[#]}" ); chars_punct=() ;;
(*) printf >&2 'Ignored arguments %s\n' "$1" ;;
esac
shift
done
#fallback
(( ${#chars[#]} )) || chars=( "${chars_alpha[#]}" )
#kick off!
increment
One of the fixes includes using printf instead of echo. With echo, your code is broken, since the strings:
-e
-n
-E
would never be printed! see help echo to understand why. Try it yourself too:
echo -e
echo -n
echo -E
don't output anything!
Use single quotes to wrap your string instead of double quotes. You won't have to have to escape any characters beside the single quote.
Hi I'm looking to write a simple script which takes an input letter and outputs it's numerical equivalent :-
I was thinking of listing all letters as variables, then have bash read the input as a variable but from here I'm pretty stuck, any help would be awesome!
#!/bin/bash
echo "enter letter"
read "LET"
a=1
b=2
c=3
d=4
e=5
f=6
g=7
h=8
i=9
j=10
k=11
l=12
m=13
n=14
o=15
p=16
q=17
r=18
s=19
t=20
u=21
v=22
w=23
x=24
y=25
z=26
LET=${a..z}
if
$LET = [ ${a..z} ];
then
echo $NUM
sleep 5
echo "success!"
sleep 1
exit
else
echo "FAIL :("
exit
fi
Try this:
echo "Input letter"
read letter
result=$(($(printf "%d\n" \'$letter) - 65))
echo $result
0
ASCII equivalent of 'A' is 65 so all you've got to do to is to take away 65 (or 64, if you want to start with 1, not 0) from the letter you want to check. For lowercase the offset will be 97.
A funny one, abusing Bash's radix system:
read -n1 -p "Type a letter: " letter
if [[ $letter = [[:alpha:]] && $letter = [[:ascii:]] ]]; then
printf "\nCode: %d\n" "$((36#$letter-9))"
else
printf "\nSorry, you didn't enter a valid letter\n"
fi
The interesting part is the $((36#$letter-9)). The 36# part tells Bash to understand the following string as a number in radix 36 which consists of a string containing the digits and letters (case not important, so it'll work with uppercase letters too), with 36#a=10, 36#b=11, …, 36#z=35. So the conversion is just a matter of subtracting 9.
The read -n1 only reads one character from standard input. The [[ $letter = [[:alpha:]] && $letter = [[:ascii:]] ]] checks that letter is really an ascii letter. Without the [[:ascii:]] test, we would validate characters like é (depending on locale) and this would mess up with the conversion.
use these two functions to get chr and ord :
chr() {
[ "$1" -lt 256 ] || return 1
printf "\\$(printf '%03o' "$1")"
}
ord() {
LC_CTYPE=C printf '%d' "'$1"
}
echo $(chr 97)
a
USing od and tr
echo "type letter: "
read LET
echo "$LET" | tr -d "\n" | od -An -t uC
OR using -n
echo -n "$LET" | od -An -t uC
If you want it to start at a=1
echo $(( $(echo -n "$LET" | od -An -t uC) - 96 ))
Explanation
Pipes into the tr to remove the newline.
Use od to change to unsigned decimal.
late to the party: use an associative array:
# require bash version 4
declare -A letters
for letter in {a..z}; do
letters[$letter]=$((++i))
done
read -p "enter a single lower case letter: " letter
echo "the value of $letter is ${letters[$letter]:-N/A}"
I'm looking for an easy way in Bash to convert a decimal number into a binary number. I have variables that need to be converted:
$ip1 $ip2 $ip3 $ip4
Is there a simple method to do this without looking at every individual number?
I would prefer not to have to write a lot of code.
You can use bc as:
echo "obase=2;$ip1" | bc
See it
Convert decimal to binary with bash builtin commands (range 0 to 255):
D2B=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1})
echo ${D2B[7]}
00000111
echo ${D2B[85]}
01010101
echo ${D2B[127]}
01111111
To remove leading zeros, e.g. from ${D2B[7]}:
echo $((10#${D2B[7]}))
111
This creates an array with 00000000 00000001 00000010 ... 11111101 11111110 11111111 with bash‘s brace expansion. The position in array D2B represents its decimal value.
See also: Understanding code ({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1})
Decimal to binary conversion in Bash:
I'm using Ubuntu 14.04 to do this.
Convert the decimals 1 through 5 to binary.
el#apollo:~$ bc <<< "obase=2;1"
1
el#apollo:~$ bc <<< "obase=2;2"
10
el#apollo:~$ bc <<< "obase=2;3"
11
el#apollo:~$ bc <<< "obase=2;4"
100
el#apollo:~$ bc <<< "obase=2;5"
101
Bonus example:
el#apollo:~$ bc <<< "obase=2;1024"
10000000000
el#apollo:~$ bc <<< "obase=2;2^128"
100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
General method for converting an integer number into another representation with another base (but base<=10 because of using digits 0..9 for representation, only):
function convertIntvalToBase () # (Val Base)
{
val=$1
base=$2
result=""
while [ $val -ne 0 ] ; do
result=$(( $val % $base ))$result #residual is next digit
val=$(( $val / $base ))
done
echo -n $result
}
e.g.
convertIntvalToBase $ip1 2 # converts $ip1 into binary representation
Defined as a function in bash:
# to Binary:
toBinary(){
local n bit
for (( n=$1 ; n>0 ; n >>= 1 )); do bit="$(( n&1 ))$bit"; done
printf "%s\n" "$bit"
}
I like to use dc for this. It's very concise:
$ n=50; dc -e "$n 2op"
110010
The commands here are as follows:
Push the number, n, on the stack, via shell expansion.
Push 2 on the stack, then use o to pop the stack and use 2 as the output radix.
p to print the top of the stack (which is just n), using the output radix set in step 2 (so print in binary).
If you want padding:
$ n=50; pad_size=16; printf "%0${pad_size}d\n" $(dc -e "$n 2op")
0000000000110010
To make #codaddict's answer a little more pretty, use this to prefix the output with 0b for "binary":
printf "0b%s\n" "$(echo "obase=2; $((num1 + num2))" | bc)"
Example:
num1=2#1111 # binary 1111 (decimal 15)
num2=2#11111 # binary 11111 (decimal 31)
printf "0b%s\n" "$(echo "obase=2; $((num1 + num2))" | bc)"
Output:
0b101110
This is decimal 46.
For details on the input base-2 formatting in bash, such as 2#1111 above, see the very end of my answer here: How to use all bash operators, and arithmetic expansion, in bash.
To have at least 8 digits in the output, use:
printf "0b%08d\n" $(echo "obase=2; $((num1 + num2))" | bc)
Source: David Rankin, in an answer to my question here.
Decimal to Binary using only Bash
Any integer number can be converted ti binary using it::
touch dec2bin.bash && chmod +x "$_" && vim "$_"
And, then copy paste the following:
#!/bin/bash
num=$1;
dec2bin()
{
op=2; ## Since we're converting to binary
quo=$(( $num/ $op)); ## quotient
rem=$(( $num% $op)); ## remainder
array=(); ## array for putting remainder inside array
array+=("$rem"); ## array expansion
until [[ $quo -eq 0 ]]; do
num=$quo; ## looping to get all remainder, untill the remainder is 0
quo=$(( $num / $op));
rem=$(( $num % $op));
array+="$rem"; ## array expansion
done
binary=$(echo "${array[#]}" | rev); ## reversing array
printf "$binary\n"; ## print array
}
main()
{
[[ -n ${num//[0-9]/} ]] &&
{ printf "$num is not an integer bruv!\n"; return 1;
} || { dec2bin $num; }
}
main;
For example:
./dec2bin.bash $var
110100100
Integer must be added!!
./dec2bin.bash 420.py
420.py is not an integer bruv!
Also, another way using python:
Much slower
python -c "print(bin(420))"
0b110100100
Hexadecimal to Binary using only Bash
Similarly, hexadecimal to binary, as follows using only bash:
#!/usr/local/bin/bash ## For Darwin :( higher bash :)
#!/bin/bash ## Linux :)
hex=$1;
hex2bin()
{
op=2; num=$((16#$hex));
quo=$(( $num/ $op));
rem=$(( $num% $op));
array=();
array+=("$rem");
until [[ $quo -eq 0 ]]; do
num=$quo;
quo=$(( $num / $op));
rem=$(( $num % $op));
array+="$rem";
done
binary=$(echo "${array[#]}" | rev);
printf "Binary of $1 is: $binary\n";
}
main()
{
[[ -n ${hex//[0-9,A-F,a-f]/} ]] &&
{ printf "$hex is not a hexa decimal number bruv!\n"; return 1;
} || { hex2bin $hex; }
}
main;
For example:
./hex2bin.bash 1aF
Binary of 1aF is: 110101111
Hex must be passed:
./hex2bin.bash XyZ
XyZ is not a hexa decimal number bruv!
toBin ()
{
printf "%08d\n" $(dc -e "$1 2op")
}
$ toBin 37
00100101