In a UNIX shell script, what can I use to convert decimal numbers into hexadecimal? I thought od would do the trick, but it's not realizing I'm feeding it ASCII representations of numbers.
printf? Gross! Using it for now, but what else is available?
Tried printf(1)?
printf "%x\n" 34
22
There are probably ways of doing that with builtin functions in all shells but it would be less portable. I've not checked the POSIX sh specs to see whether it has such capabilities.
echo "obase=16; 34" | bc
If you want to filter a whole file of integers, one per line:
( echo "obase=16" ; cat file_of_integers ) | bc
Hexidecimal to decimal:
$ echo $((0xfee10000))
4276158464
Decimal to hexadecimal:
$ printf '%x\n' 26
1a
bash-4.2$ printf '%x\n' 4294967295
ffffffff
bash-4.2$ printf -v hex '%x' 4294967295
bash-4.2$ echo $hex
ffffffff
Sorry my fault, try this...
#!/bin/bash
:
declare -r HEX_DIGITS="0123456789ABCDEF"
dec_value=$1
hex_value=""
until [ $dec_value == 0 ]; do
rem_value=$((dec_value % 16))
dec_value=$((dec_value / 16))
hex_digit=${HEX_DIGITS:$rem_value:1}
hex_value="${hex_digit}${hex_value}"
done
echo -e "${hex_value}"
Example:
$ ./dtoh 1024
400
Try:
printf "%X\n" ${MY_NUMBER}
In my case, I stumbled upon one issue with using printf solution:
$ printf "%x" 008
bash: printf: 008: invalid octal number
The easiest way was to use solution with bc, suggested in post higher:
$ bc <<< "obase=16; 008"
8
In zsh you can do this sort of thing:
% typeset -i 16 y
% print $(( [#8] x = 32, y = 32 ))
8#40
% print $x $y
8#40 16#20
% setopt c_bases
% print $y
0x20
Example taken from zsh docs page about Arithmetic Evaluation.
I believe Bash has similar capabilities.
xd() {
printf "hex> "
while read i
do
printf "dec $(( 0x${i} ))\n\nhex> "
done
}
dx() {
printf "dec> "
while read i
do
printf 'hex %x\n\ndec> ' $i
done
}
# number conversion.
while `test $ans='y'`
do
echo "Menu"
echo "1.Decimal to Hexadecimal"
echo "2.Decimal to Octal"
echo "3.Hexadecimal to Binary"
echo "4.Octal to Binary"
echo "5.Hexadecimal to Octal"
echo "6.Octal to Hexadecimal"
echo "7.Exit"
read choice
case $choice in
1) echo "Enter the decimal no."
read n
hex=`echo "ibase=10;obase=16;$n"|bc`
echo "The hexadecimal no. is $hex"
;;
2) echo "Enter the decimal no."
read n
oct=`echo "ibase=10;obase=8;$n"|bc`
echo "The octal no. is $oct"
;;
3) echo "Enter the hexadecimal no."
read n
binary=`echo "ibase=16;obase=2;$n"|bc`
echo "The binary no. is $binary"
;;
4) echo "Enter the octal no."
read n
binary=`echo "ibase=8;obase=2;$n"|bc`
echo "The binary no. is $binary"
;;
5) echo "Enter the hexadecimal no."
read n
oct=`echo "ibase=16;obase=8;$n"|bc`
echo "The octal no. is $oct"
;;
6) echo "Enter the octal no."
read n
hex=`echo "ibase=8;obase=16;$n"|bc`
echo "The hexadecimal no. is $hex"
;;
7) exit
;;
*) echo "invalid no."
;;
esac
done
This is not a shell script, but it is the cli tool I'm using to convert numbers among bin/oct/dec/hex:
#!/usr/bin/perl
if (#ARGV < 2) {
printf("Convert numbers among bin/oct/dec/hex\n");
printf("\nUsage: base b/o/d/x num num2 ... \n");
exit;
}
for ($i=1; $i<#ARGV; $i++) {
if ($ARGV[0] eq "b") {
$num = oct("0b$ARGV[$i]");
} elsif ($ARGV[0] eq "o") {
$num = oct($ARGV[$i]);
} elsif ($ARGV[0] eq "d") {
$num = $ARGV[$i];
} elsif ($ARGV[0] eq "h") {
$num = hex($ARGV[$i]);
} else {
printf("Usage: base b/o/d/x num num2 ... \n");
exit;
}
printf("0x%x = 0d%d = 0%o = 0b%b\n", $num, $num, $num, $num);
}
For those who would like to use variables, first export it by running:
export NUM=100
Then run:
printf "%x\n" $NUM
Else, you can you can ignore the use case of the variables and run it directly as shown below:
printf "%x\n" 100
NB:Substitute NUM with the variable name of your choice.
Exporting makes it an environmental variable(global).
Wow, I didn't realize that printf was available at the shell!
With that said, I'm surprised no-one commented about putting the printf into a shell script (which then you could put in your personal bin directory if you wanted).
echo "printf "0x%x\n" $1" > hex
chmod +x hex
Now just run:
./hex 123
It returns:
0x7b
Related
I'm developing a script on which I have a hex string 31323334353637383930313233 and I want to transform it into ASCII. Desired output is 1234567890123.
I already have it working using:
echo "31323334353637383930313233" | xxd -r -p
or
echo "31323334353637383930313233" | perl -pe 's/(..)/chr(hex($1))/ge'
But the point is try to use the minimum possible requirements for the script. I want it working in suse, fedora, debian, ubuntu, arch, etc... It seems the xxd command is included in vim package. I'm wondering if there is a way to achieve this using only awk or any internal Linux tool which is going to be present by default in all Linux systems.
Found this script here:
#!/bin/bash
function hex2string () {
I=0
while [ $I -lt ${#1} ];
do
echo -en "\x"${1:$I:2}
let "I += 2"
done
}
hex2string "31323334353637383930313233"
echo
You may change the line hex2string "31323334353637383930313233" so that it takes the hex value from parameters, that is:
#!/bin/bash
function hex2string () {
I=0
while [ $I -lt ${#1} ];
do
echo -en "\x"${1:$I:2}
let "I += 2"
done
}
hex2string "$1"
echo
So when executed as:
./hexstring.sh 31323334353637383930313233
It will provide the desired ascii output.
NOTE: Can't test if it works in all Linux systems.
Using gawk, from HEX to ASCII
$ gawk '{
gsub(/../,"0x& ");
for(i=1;i<=NF;i++)
printf("%c", strtonum($i));
print ""
}' <<<"31323334353637383930313233"
1234567890123
Using any awk
$ cat hex2asc_anyawk.awk
BEGIN{
split("0 1 2 3 4 5 6 7 8 9 A B C D E F", d, / /)
for(i in d)Decimal[d[i]]=i-1
}
function hex2dec(hex, h,i,j,dec)
{
hex = toupper(hex);
i = length(hex);
while(i)
{
dec += Decimal[substr(hex,i,1)] * 16 ^ j++
i--
}
return dec;
}
{
gsub(/../,"& ");
for(i=1;i<=NF;i++)
printf("%d",hex2dec($i));
print ""
}
Execution
$ awk -f hex2asc_anyawk.awk <<<"31323334353637383930313233"
1234567890123
Explanation
Steps :
Get the decimal equivalent of hex from table.
Multiply every digit with 16 power of digit location.
Sum all the multipliers.
Example :
BEGIN{
# Here we created decimal conversion array, like above table
split("0 1 2 3 4 5 6 7 8 9 A B C D E F", d, / /)
for(i in d)Decimal[d[i]]=i-1
}
function hex2dec(hex, h,i,j,dec)
{
hex = toupper(hex); # uppercase conversion if any A,B,C,D,E,F
i = length(hex); # length of hex string
while(i)
{
# dec var where sum is stored
# substr(hex,i,1) gives 1 char from RHS
# multiply by 16 power of digit location
dec += Decimal[substr(hex,i,1)] * 16 ^ j++
i-- # decrement by 1
}
return dec;
}
{
# it modifies record
# suppose if given string is 31323334353637383930313233
# after gsub it becomes 31 32 33 34 35 36 37 38 39 30 31 32 33
# thus re-evaluate the fields
gsub(/../,"& ");
# loop through fields , NF gives no of fields
for(i=1;i<=NF;i++)
# convert from hex to decimal
# and print equivalent ASCII value
printf("%c",hex2dec($i));
# print newline char
print ""
}
Meaning of dec += Decimal[substr(hex,i,1)] * 16 ^ j++
dec += Decimal[substr(hex,i,1)] * 16 ^ j++
^ ^ ^
| | |
| | 2.Multiply every digit with 16 power of digit location.
| |
| 1.Gives decimal equivalent of hex
|
|
3. Sum all the multipliers
here's a special cheating trick for u - due to ingenuity of how they originally mapped decimal digits to bytes, their hex are all x3[0-9],
so therefore, if u already know they would decode out to digits and nothing else, here's a fast shortcut :
echo "31323334353637383930313233" |
mawk 'gsub("..","_&") + gsub("_3",_)^_'
1234567890123
if it's already URL-percent-encoded, then it's even simpler :
echo '%31%32%33%34%35%36%37%38%39%30%31%32%33' |
mawk 'gsub("%3",_)^_'
or
gawk ++NF FS='%3' OFS=
1234567890123
This specialized approach can handle hex of absolutely any arbitrary size, even for awks that don't have built-in support for bigints
TL;DR : don't "do math" when none is needed
Alternate (g)awk solution:
echo "31323334353637383930313233" | awk 'RT{printf "%c", strtonum("0x"RT)}' RS='[0-9]{2}'
I'm new to shell scripting, I have written a small script to check if the input is in the specified (integer) range:
echo "Enter number btw 50 and 100:"
read num
if [ $num -le 100 -a $num -ge 50 ];
then
echo "you are in limits."
else
echo "you are out of limits."
fi
How can I check if a number is in a real/floating range?
You can't with sh/ksh1/bash(/zsh?) -- integer arithmetic only.
You can do something like
if [ "$(echo "50 <= $num && $num <= 100" | bc)" = "1" ]; then
echo "you are in limits."
else
echo "you are out of limits."
fi
(1 - ksh93 can do floating point arithmetic)
please help me with this problem, i have an array witch includes 1000 lines with number which are treated as strings and i want for all of them to reverse them one by one, my problem is how to reverse them because i have to use ksh or else with bash or something it would be so easy..... what i have now is this, but
rev="$rev${copy:$y:1}" doesnt work in ksh.
i=0
while [[ $i -lt 999 ]]
do
rev=""
var=${xnumbers[$i]}
copy=${var}
len=${#copy}
y=$(expr $len - 1)
while [[ $y -ge 0 ]]
do
rev="$rev${copy:$y:1}"
echo "y = " $y
y=$(expr $y - 1)
done
echo "i = " $i
echo "rev = " $rev
#xnumbers[$i]=$(expr $xnumbers[$i] "|" $rev)
echo "xum = " ${xnumbers[$i]}
echo "##############################################"
i=$(expr $i + 1)
done
I am not sure why we cannot use built in rev function.
$ echo 798|rev
897
You can also try:
$ echo 798 | awk '{ for(i=length;i!=0;i--)x=x substr($0,i,1);}END{print x}'
897
If, you can print the contents of the array to a file, you can then process the file with this awk oneliner.
awk '{s1=split($0,A,""); line=""; for (i=s1;i>0;i--) line=line A[i];print line}' file
Check this!!
other_var=`echo ${xnumbers[$i]} | awk '{s1=split($0,A,""); line=""; for (i=s1;i>0;i--) line=line A[i];print line}'`
I have tested this on Ubuntu with ksh, same results:
number="789"
other_var=`echo $number | awk '{s1=split($0,A,""); line=""; for (i=s1;i>0;i--) line=line A[i];print line}'`
echo $other_var
987
You could use cut, paste and rev together, just change printf to cat file.txt:
paste -d' ' <(printf "%s data\n" {1..100} | cut -d' ' -f1) <(printf "%s data\n" {1..100} | cut -d' ' -f2 |rev)
Or rev alone if, it's not a numbered file as clarified by the OP.
I would like to create shell script that echos a + if the inputted integer is a positive, echos a "-" if the inputted integer is a negative, and echos a "0" if its not any of those.
So my script is like this:
echo "Enter an Integer"
read declare -i var1
if((var1>0))
then
echo +
elif (( var1<0))
then echo -
else echo 0
fi
I keep getting a bad variable name error and var1 not found.
I got the script to work by separating the read and declare lines.
echo "Enter an Integer"
declare -i var1
read var1
if((var1>0))
then
echo +
elif (( var1<0))
then echo -
else echo 0
fi
Console log (running on OS X):
$ bash e.sh
Enter an Integer
972
+
$ bash e.sh
Enter an Integer
-32
-
$ bash e.sh
Enter an Integer
0
0
I believe you want to read and then test your values. Something like
echo "Enter an Integer"
read var1
if [[ "$var1" -gt 0 ]]
then
echo +
elif [[ "$var1" -lt 0 ]]
then
echo -
else
echo 0
fi
works here.
I am new to UNIX ... I am trying to write a bash script that takes two integers from the user and prints out the even numbers between these two numbers using an if condition. I am stuck on the nested if "an unexpected token near else" error message appears. I do not know what the error is about. Any help?
This is what I have done so far:
echo plz enter first number
read n1
echo plz enter second number
read n2
start=$n1
end=$n2
if [ start < end ] then
for (c=start;c<=end;c++)
do
if [ $((c % 2 )) -eq 0 ]; then
echo $c
fi
done
else
echo "not bigger"
fi
I think I would recommend a different approach:
((start % 2)) && ((start = 1 + start))
while ((start < end))
do
echo ${start}
(( start += 2))
done
I have tried like this:-
echo "Enter first number"
read first
echo "Enter second number"
read second
start=$first
endLine=$second
while [ $start -le $endLine ]
do
if [ $((start % 2 )) -eq 0 ]
then
echo $start "is an even number"
#else
# echo $start "is an odd number"
fi
start=`expr $start + 1`
done
You need to insert either a semicolon or a newline before the first "then":
if [ start < end ] ; then
^