I am new to shell script
I Have tried to multiply two hex numbers in shell script in the following manner.
initial= expr 0x10000 \* 0x22
echo $initial
While running the script,The following error is seen.
expr: non-numeric argument
Can someone point out what might the mistake?
No need to expr, use $(( )) just like this:
$ echo $((0x10000 * 0x22))
2228224
Or you can use bc like this, indicating input is hex (ibase) and desired output also in hex (obase) (as Adobe's deleted answer states):
$ echo "ibase=16; obase=16; 10000*22" | bc
09 11 05 16 20
$ echo "ibase=16; 10000*22" | bc
2228224
Related
Yes, I have seen Bash shell Decimal to Binary base 2 conversion , but I cannot tell how to do the following easily:
Say, I want to convert the decimal number 47803625 to a 32-bit binary string, that is 0b00000010110110010110110011101001. Closest I could get to is this:
$ printf "0b%32s\n" $(echo "obase=2; 47803625" | bc)
0b 10110110010110110011101001
... however, this pads the string on the left with spaces, not zeroes.
(sidenote: if I try the above with the %d format specifier, it fails:
$ printf "0b%32d\n" $(echo "obase=2; 47803625" | bc)
bash: printf: warning: 10110110010110110011101001: Numerical result out of range
0b 9223372036854775807
)
What would be an easy way to do this kind of a conversion from the command line? I'd be fine with a Python or other solution as well - as long as it is a one-liner I can type in a terminal ...
Eh, found it - simply prepend a 0 to the %32s format specifiers, then zeroes are used to left pad, even for a string %s format specifier:
$ printf "0b%032s\n" $(echo "obase=2; 47803625" | bc)
0b00000010110110010110110011101001
I'm new in bash and i'd like to know why my script doesn t work the way i'd like it work..
I have this bash script:
#!/bin/bash
read n
var=($(cat))
bim=${var[*]}
toto=$(echo $bim | sed 's/ /+/g' | bc)
echo $toto
bobo=$(($toto/$n | bc -l))
echo $bobo | awk '{printf "%.3f\n", $1}'
This is supposed to add up all the values that "cat" has stored in an array and divide the total by the first value that "read" reads. And the result should return me a decimal value of three decimal places. However, it only returns a round number to me when I use bc -l! And when I use awk '{printf% .3f ", $ 1}' it prints .000!
Do you know why?
Thanks
bash only does integer arithmetics and the arithmetic expansion
bobo=$(($toto/$n | bc -l))
will not do what you think. The pipe sign in | bc -l is not a pipe sign. It's a bitwise OR. Broken down:
$toto is interpreted as a variable (as would toto)
$n is interpreted as a variable (as would n)
| is bitwise OR
bc is interpreted as a variable (with the value 0)
- is interpreted as a minus sign (in an arithmetic expression)
l is interpreted as a variable (with the value 0)
So, it becomes $toto/$n | 0 -0 which is the same as the integer division toto/n ($ is not needed for variables in arithmetic expansions).
You could instead use bc for the division too, but you need to set the scale in bc. Here's an example setting it to 3 before performing the division:
bobo=$(echo "scale=3;$toto/$n" | bc)
Note that you don't need to echo this through awk.
Just echo $bobo and you should get the result you want.
Example input:
3
11.3
9
8
Output after having applied the suggested changes:
28.3
9.433
The split command produces by default a file suffix of the form "aa" "ab" ... "by" "bz"...
However in a script, I need to recover this suffix, starting from the file number as an integer (without globbing).
I wrote the following code, but maybe bash wizards here have a more concise solution?
alph="abcdefghijklmnopqrstuvwxyz"
for j in {0..100}; do
# Convert j to the split suffix (aa ab ac ...)
first=$(( j / 26 ))
sec=$(( j % 26 ))
echo "${alph:$first:1}${alph:$sec:1}"
done
Alternatively, I could use bc with the obase variable, but it only outputs one number in case j<26.
bc <<< 'obase=26; 5'
# 05
bc <<< 'obase=26; 31'
# 01 05
Use this Perl one-liner and specify the file numbers (0-indexed) as arguments, for example:
perl -le 'print for ("aa".."zz")[#ARGV]' 0 25 26
Output:
aa
az
ba
The Perl one-liner uses these command line flags:
-e : Tells Perl to look for code in-line, instead of in a file.
-l : Strip the input line separator ("\n" on *NIX by default) before executing the code in-line, and append it when printing.
#ARGV : array of the command-line arguments.
From top of my head, depending on 97 beeing ASCII a:
printf "\x$(printf %x $((97+j/26)))\x$(printf %x $((97+j%26)))\n"
printf "\\$(printf %o $((97+j/26)))\\$(printf %o $((97+j%26)))\n"
awk "BEGIN{ printf \"%c%c\\n\", $((97+j/26)), $((97+j%26))}" <&-
printf %x $((97+j/26)) $((97+j%26)) | xxd -r -p
You could also just write without temporary variables:
echo "${alph:j/26:1}${alph:j%26:1}"
In my use case, I do want to generate the full list
awk should be fast:
awk 'BEGIN{ for (i=0;i<=100;++i) printf "%c%c\n", 97+i/26, 97+i%26}' <&-
In a text file, test.txt, I have the next information:
sl-gs5 desconnected Wed Oct 10 08:00:01 EDT 2012 1001
I want to extract the hour of the event by the next command line:
hour=$(grep -n sl-gs5 test.txt | tail -1 | cut -d' ' -f6 | awk -F ":" '{print $1}')
and I got "08". When I try to add 1,
14 echo $((hour+1))
I receive the next error message:
./test2.sh: line 14: 08: value too great for base (error token is "08")
If variables in Bash are untyped, why?
See ARITHMETIC EVALUATION in man bash:
Constants with a leading 0 are interpreted as octal numbers.
You can remove the leading zero by parameter expansion:
hour=${hour#0}
or force base-10 interpretation:
$((10#$hour + 1))
what I'd call a hack, but given that you're only processing hour values, you can do
hour=08
echo $(( ${hour#0} +1 ))
9
hour=10
echo $(( ${hour#0} +1))
11
with little risk.
IHTH.
You could also use bc
hour=8
result=$(echo "$hour + 1" | bc)
echo $result
9
Here's an easy way, albeit not the prettiest way to get an int value for a string.
hour=`expr $hour + 0`
Example
bash-3.2$ hour="08"
bash-3.2$ hour=`expr $hour + 0`
bash-3.2$ echo $hour
8
In Short: In order to deal with "Leading Zero" numbers (any 0 digit that comes before the first non-zero) in bash
- Use bc An arbitrary precision calculator language
Example:
a="000001"
b=$(echo $a | bc)
echo $b
Output: 1
From Bash manual:
"bc is a language that supports arbitrary precision numbers with interactive execution
of statements. There are some similarities in the syntax to the C programming lan-
guage. A standard math library is available by command line option. If requested, the
math library is defined before processing any files. bc starts by processing code from
all the files listed on the command line in the order listed. After all files have
been processed, bc reads from the standard input. All code is executed as it is read.
(If a file contains a command to halt the processor, bc will never read from the standard input.)"
Since hours are always positive, and always 2 digits, you can set a 1 in front of it and subtract 100:
echo $((1$hour+1-100))
which is equivalent to
echo $((1$hour-99))
Be sure to comment such gymnastics. :)
The leading 0 is leading to bash trying to interpret your number as an octal number, but octal numbers are 0-7, and 8 is thus an invalid token.
If I were you, I would add some logic to remove a leading 0, add one, and re-add the leading 0 if the result is < 10.
How about sed?
hour=`echo $hour|sed -e "s/^0*//g"`
I'm trying to convert a hex string to binary. I'm using:
echo "ibase=16; obase=2; $line" | BC_LINE_LENGTH=9999 bc
It is truncating the leading zeroes. That is, if the hex string is 4F, it is converted to 1001111 and if it is 0F, it is converted to 1111. I need it to be 01001111 and 00001111
What can I do?
The output from bc is correct; it simply isn't what you had in mind (but it is what the designers of bc had in mind). If you converted hex 4F to decimal, you would not expect to get 079 out of it, would you? Why should you get leading zeroes if the output base is binary? Short answer: you shouldn't, so bc doesn't emit them.
If you must make the binary output a multiple of 8 bits, you can add an appropriate number of leading zeroes using some other tool, such as awk:
awk '{ len = (8 - length % 8) % 8; printf "%.*s%s\n", len, "00000000", $0}'
Pure Bash solution (beside bc):
paddy()
{
how_many_bits=$1
read number
zeros=$(( $how_many_bits - ${#number} ))
for ((i=0;i<$zeros;i++)); do
echo -en 0
done && echo $number
}
Usage:
>bc <<< "obase=2;ibase=16; 20" | paddy 8
00100000
You can pipe to awk like this:
echo "ibase=16; obase=2; $line" | BC_LINE_LENGTH=9999 bc | awk '{ printf "%08d\n", $0 }'
You can do it in python:
line=4F
python -c "print ''.join([bin(int(i, 16))[2:].zfill(4) for i in '$line'])"
result:
'01001111'
What's frustrating is the bc expects the input to be zero padded but doesn't provide a similar output option. Here's another alternative using sed:
sed 's_0_0000_g; s_1_0001_g; s_2_0010_g; s_3_0011_g;
s_4_0100_g; s_5_0101_g; s_6_0110_g; s_7_0111_g;
s_8_1000_g; s_9_1001_g; s_[aA]_1010_g; s_[bB]_1011_g;
s_[cC]_1100_g; s_[dD]_1101_g; s_[eE]_1110_g; s_[fF]_1111_g;'
You can use seq and sed to help you pād:
function paddington(){
PADDLE=8; while read IN; do
seq -f '0' -s '' 1 $PADDLE | \
sed "s/0\{${#IN}\}\$/$IN/"
done
}
bc <<< "ibase=16; obase=2; 4F; 1E; 0F" | paddington
The output:
01001111
00011110
00001111
You can use printf to left-pad the result with zeros if the result's length is not a multiple of four:
hex_nr=2C8B; hex_len=${#hex_nr}; binary_nr=$(bc <<< "obase=2;ibase=16;$hex_nr"); \
bin_length=$(( hex_len * 4 )); printf "%0${bin_length}d\n" $binary_nr
will result in
0010110010001011
instead of bc's output of
10110010001011