Do math on output of cat - bash

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

Related

Shell decimal to hexadecimal

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

Is it possible to write the binary representation of a number into a file using bash sript?

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.

I am getting expr syntax errors in bash shell for a simple program

#!/bin/bash
clear
echo "Enter a number"
read a
s = 0
while [ $a -gt 0 ]
do
r = ` expr $a % 10 `
s = ` expr $s + $r `
a = ` expr $a / 10 `
done
echo "sum of digits is = $s"
This is my code guys .
I am getting a bunch of expr syntax errors.
I am using the bash shell.
Thanks!
Your error is caused by the spaces surrounding the = in the assignments, the following replacements should work (I prefer $() to using backticks since they're much easier to nest):
s=0
r=$(expr $a % 10)
s=$(expr $s + $r)
a=$(expr $a / 10)
For example, s = 0 (with the spaces) does not set the variable s to zero, rather it tries to run the command s with the two arguments, = and 0.
However, it's not really necessary to call the external expr1 to do mathematical manipulation and capture the output to a variable. That's because bash itself can do this well enough without resorting to output capture (see ARITHMETIC EVALUATION in the bash man page):
#!/bin/bash
clear
read -p "Enter a number: " number
((sum = 0))
while [[ $number -gt 0 ]]; do
((sum += number % 10))
((number /= 10))
done
echo "Sum of digits is $sum"
You'll notice I've made some other minor changes which I believe enhances the readability, but you could revert back to the your original code if you wish and just use the ((expression)) method rather than expr.
1 If you don't mind calling external executables, there's no need for a loop in bash, you could instead use sneakier methods:
#!/bin/bash
clear
read -p "Enter a number: " number
echo "Sum of digits is $(grep -o . <<<$number | paste -sd+ | bc)"
But, to be brutally honest, I think I prefer the readable solution :-)

Simple bash script (input letter output number)

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}"

Bash shell Decimal to Binary base 2 conversion

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

Resources