bash convert hex string to integer code using printf failing - bash

I am iterating over hex digits in a string using a for loop. I can extract each hex digit ok, but my code to convert to a number is giving strange values. How can I fix this code?
Script:
#!/bin/bash
mystring="5e51584a4c"
for (( i = 0; i < ${#mystring}; i = i + 2)); do
snumber="${mystring:i:2}"
printf "number as string=%s\n" $snumber
number=$(printf "%x" "'${mystring:i:2}")
printf "number=%d\n" $number
done
I am getting this output:
number as string=5e
number=35
number as string=51
number=35
number as string=58
number=35
number as string=4a
number=34
number as string=4c
number=34

You don't need printf in this case, you can use bash's $((base#number)) construct.
#!/bin/bash -
mystring="5e51584a4c"
for ((i=0; i<${#mystring}; i+=2)); do
snumber="${mystring:i:2}"
echo "number as string=${snumber}"
echo "number=$((16#${snumber}))"
done

Hex numbers need to have 0x prefix:
printf "%d\n" 0x5e
Outputs:
94
mystring="5e51584a4c"
for (( i = 0; i < ${#mystring}; i = i + 2)); do
snumber="${mystring:i:2}"
printf "number=%d\n" 0x$snumber
done

Your printf command is printing the character code of the first digit in each substring, because you have the ' prefix to the argument. You want a 0x prefix instead.
When printing as hexadecimal, you can use %#x conversion to make printf emit the leading 0x prefix needed by the next usage:
number=$(printf '%#x' "0x${mystring:i:2}")
# ^^^ ^^

Related

printf bash - print text in the middle of existing line surrounded by marks

It doesn't seem that simple. At least for me.
I need to have a variable in printf text. It's something like:
FOO="User data"
+++++++++++++++++++ $FOO +++++++++++++++++++++
Would output
+++++++++++++++++++ User Data +++++++++++++++++++++
But
FOO="Fooooooo barrrr"
+++++++++++++++++++ $FOO +++++++++++++++++++++
Should output
++++++++++++++++ Fooooooo barrrr ++++++++++++++++++
And
FOO="Foooooooooooooooooooo barrrrr"
+++++++++++++++++++ $FOO +++++++++++++++++++++
Should be
+++++++++ Foooooooooooooooooooo barrrrr +++++++++++
As you can see I need a variable to be in the middle of n-length line, surrounded by + mark. How to achieve that using printf and other default-available commands?
(Debian 8)
declare -i x1 x2 x3 width
foo="User data"
width=50 # total width
x2=${#foo}+2 # length of $foo and 2 whitespaces
x1=(50-x2)/2 # length of first part
x3=$width-x1-x2 # length of last part
for ((i=1;i<=$x1;i++)); do echo -n "+"; done
echo -n " $foo "
for ((i=1;i<=$x3;i++)); do echo -n "+"; done
Output:
+++++++++++++++++++ User data ++++++++++++++++++++
With foo="stackoverflow.com":
+++++++++++++++ stackoverflow.com ++++++++++++++++
#!/usr/bin/env bash
linelen=100
char="+"
text=$1
len=$(echo -n $text | wc -m)
fillerlen=$((($linelen - $len - 2) / 2))
filler=$(printf "$char%.0s" $(seq 1 $fillerlen))
echo $filler $text $filler
In the format string for printf, you can specify the "precision" of a string with %${p}s, where $p is the precision. You can take advantage of that by printing nothing (expanding to a space) the desired number of times and then translating the spaces into "+":
$ p=10
$ printf "%${p}s\n" | tr ' ' +
++++++++++
This function takes the length of your line and the string you want to put in its centre, then prints it padded with plus signs:
pad () {
len=$1
string=$2
# ${#string} expands to the length of $string
n_pad=$(( (len - ${#string} - 2) / 2 ))
printf "%${n_pad}s" | tr ' ' +
printf ' %s ' "$string"
printf "%${n_pad}s\n" | tr ' ' +
}
Works like this:
$ pad 50 Test
++++++++++++++++++++++ Test ++++++++++++++++++++++
$ pad 50 "A longer string to be padded"
++++++++++ A longer string to be padded ++++++++++
Notice how you have to quote strings consisting of more than one word, or only the first one will be used.
If the length of your line is not divisible by 2, the padding will be rounded down, but will always be symmetrical.
Try this :
#!/bin/bash
n=50; # You can change the value of n as you please.
var="fooo baar";
size=${#var}
n=$(( n - size ))
n=$(( n / 2 ))
s=$(printf "%-${n}s" "*")
echo "${s// /*} "$var" ${s// /*}" #white-spaces included here.

increment a letter sequence to represent a whole number where a=0 and z=25

I have tried several different search terms but have not found exactly what I want, I am sure there is already an answer for this so please point me to it if so.
I would like to understand how to increment a letter code given a standard number convention in a bash script.
Starting with AAAA=0 or with leading zerosAAAA=000000 (26x26x26x26) I would like to increment the value with a a positive single digit each time, so aaab=000001,aaac=000002 and aaba=000026 and aaaca=000052 etc.
Thanks Art!
I guess this is what you want
echo {a..z}{a..z}{a..z}{a..z} | tr ' ' '\n' | nl
will be too long, perhaps test with this first
echo {a..z}{a..z} | tr ' ' '\n' | nl
if you don't need the line numbers remove last pipe and nl
If you need the output in xxxx=nnnnnn format, you can use awk
echo {a..z}{a..z}{a..z}{a..z} | tr ' ' '\n' | awk '{printf "%s=%06d\n", $0, NR-1}'
aaaa=000000
aaab=000001
aaac=000002
aaad=000003
aaae=000004
aaaf=000005
aaag=000006
aaah=000007
aaai=000008
aaaj=000009
...
zzzv=456971
zzzw=456972
zzzx=456973
zzzy=456974
zzzz=456975
Fast
If you are aiming for speed and simplicity:
#!/bin/bash
i=0
for text in {a..z}{a..z}{a..z}{a..z}; do
printf '%06d %5.5s\n' "$i" "$text"
(( i++ ))
done
Precise
Aiming at having a function that convert any number to the character string:
We must Understand that what you are describing is a number written in base 26, using the character a as 0, b as 1, c as 3, etc.
Thus, aaaa means 0000, aaab means 0001, aaac means 0002, .... aaaz means 0025
and aaba means 0026, aaca means 0052.
bc could do the base conversion directly (as numbers):
$ echo 'obase=26; 199'|bc
07 17
The 7th letter is: a0, b1, c2, d3, e4, f5, g6, (h)7,
the 17th letter is (r).
If we set the variable list to: list=$(printf '%s' {a..z}) or list=abcdefghijklmnopqrstuvwxyz
We could get each letter from the number with: ${list:7:1} and ${list:17:1}
$ echo "${list:7:1} and ${list:17:1}"
h and r
$ printf '%s' "${list:7:1}" "${list:17:1}" # Using printf:
hr
Script
All together inside an script, is:
#!/bin/bash
list=$(printf '%s' {a..z})
getletters(){
local numbers
numbers="$(echo "obase=26; $1"|bc)"
for number in $numbers; do
printf '%s' "${list:10#$number:1}";
done;
echo;
}
count=2
limit=$(( 26**$count - 1 ))
for (( i=0; i<=$limit; i++)); do
printf '%06d %-5.5s\n' "$i" "$(getletters "$i")"
done
Please change count from 2 to 4 to get the whole list. Be aware that such list is more than half a million lines: The limit is 456,975 and will take some time.
With perl, you can ++ a string to increment the letter:
for (my ($n,$s) = (0,"aaaa"); $n < 200; $n++, $s++) {
printf "%s=%0*d\n", $s, length($s), $n;
}
outputs
aaaa=0000
aaab=0001
aaac=0002
aaad=0003
...
aaby=0050
aabz=0051
aaca=0052
aacb=0053
...
aahp=0197
aahq=0198
aahr=0199

bash split a variable into two or more based on character length

I built a script for SMS autoresponder, my goal is that when an sms content has more than 160 of character length, it splits the content into two or more variables then send them separately.
myvar="this variable has more than ten character length"
That variable has 48 of character length, how do I print that variable from length 1 to length 25 and length 26 to 48 ? So i'll have 2 variables in the end and send those variables with sms:
firstvar="this variable has more th"
secondvar="an ten character length"
I know there's a command split but my openwrt doesn't support that command, so I have to find another way to do that.
Bash can split a variable into substrings using it's substitution rules.
echo ${variable:4:8}
Will display eight characters starting at offset four. The offset starts at zero.
In general:
${parameter:offset:length}
this snippet should help you:
myvar="this variable has more than ten character length"
size=${#myvar}
if [ $size -gt 25 ]; then
firstvar=${myvar:0:25}
secondvar=${myvar:26:size}
echo "$firstvar"
echo "$secondvar"
fi
A pure Bash possibility, no external tools (hence only depends on Bash and no other specific third-party tools) and no subshells:
#!/bin/bash
mysms="this variable has more than ten character length"
maxlength=25
sms_tmp=$mysms
sms_ary=()
while [[ $sms_tmp ]]; do
sms_ary+=( "${sms_tmp::maxlength}" )
sms_tmp=${sms_tmp:maxlength}
done
# At this point, you have your sms split in the array sms_ary:
# You can print them, one per line:
printf '%s\n' "${sms_ary[#]}"
# You can print them, one per line, with header:
printf -- '--START SMS-- %s --END SMS--\n' "${sms_ary[#]}"
# You can print them, space padded (spaces on the right):
printf -- "--START SMS-- %-$(maxlength}s --END SMS--\n" "${sms_ary[#]}"
# You can print them, space padded (spaces on the left):
printf -- "--START SMS-- %${maxlength}s --END SMS--\n" "${sms_ary[#]}"
# You can loop through them:
for sms in "${sms_ary[#]}"; do
printf 'Doing stuff with SMS: %s\n' "$sms"
done
# You can loop through them with index (C-style loop):
for ((i=0;i<${#sms_ary[#]};++i)); do
printf 'This is SMS #%d at index %d: %s\n' "$((i+1))" "$i" "${sms_ary[i]}"
done
# You can loop through them (using array key as variable):
n=1
for i in "${!sms_ary[#]}"; do
printf 'This is SMS #%d at index %d: %s\n' "$((n++))" "$i" "${sms_ary[i]}"
done
# Here's the number of SMS:
printf 'That was fun. There were %d chunks of SMS\n' "${#sms_ary[#]}"
Another way to split your string:
#!/bin/bash
mysms="this variable has more than ten character length"
maxlength=25
sms_ary=()
for ((i=0;i<${#mysms};i+=maxlength)); do
sms_ary+=( "${mysms:i:maxlength}" )
done
# Same as before, at this point you have your chunks in array sms_ary

How to pad text in bash

I want to create a text file using bash shell in the following
+9990000001
+9990000002
+9990000003
+9990000004
...
The first four chars/digits are fixed (+999) and the other 7 digits starts from 0000001 to 9999999
I have the following code but it does not do 00000000 padding.
startnum=0;
endnum=9999999;
for (( i=$startnum; i<=$endnum; i++ )) ; do
echo $i ;
done
How would I also do the prefix +999?
You need to use printf to pad 0s:
startnum=1
endnum=9999999
for (( i=startnum; i<=endnum; i++ )) ; do
printf "+999%07d\n" $i
done
Output:
+9990000001
+9990000002
+9990000003
+9990000004
+9990000005
+9990000006
...
You can write:
startnum=90000000
endnum=99999999
for (( i = startnum; i <= endnum; i++ )) ; do
echo +99$i
done
(I would have suggested simply putting all three nines at the beginning, but 1010−1 is outside the range of 32-bit signed integers, so I'm not sure the arithmetic part would work.)
My version:
seq -f "+%10.0f" 9990000001 9999999999
or
seq -f "+999%07.0f" 9999999

Corrupting a string of random characters using bash

I have a string which looks something like
/6435045045/hpdqbhflyuhv_EOlXG
I want to corrupt the string by changing a few characters. I did
$string | sed 's/E/A/g'
it worked fine but the string is dynamic and now the string is generated without an 'E' in it.
Is it possible to corrupt the string by replacing first x characters with some random x characters using bash ??
> string="/6435045045/hpdqbhflyuhv_EOlXG"
> n=8
> m=`expr ${#string} - $n`
> string="`< /dev/urandom tr -dc A-Za-z0-9_ | head -c$n``echo $string | tail -c$m`"
> echo $string
oZeHdOD_45/hpdqbhflyuhv_EOlXG
http://nixcraft.com/shell-scripting/17295-generate-random-chars-bash.html
http://www.unixcl.com/2008/03/find-string-length-bash.html
Subtract two variables in Bash
How rotten do you want to make the string?
tr 'A-Za-z' 'N-ZA-Mn-za-m' <<<$string
This is one script in bash. It allows you to specify a character range and the number of times the contents of the string would be changed.
#!/bin/bash
# syntax: corrupt <string> <times> <character range in ascii codes>
#
function corrupt {
local STRING=$1
local STRING_LENGTH=${#1}
local TIMES=$2
local RANGE=("${#:3}")
local RANGE_LENGTH="${#RANGE[#]}"
local CHARS=()
local C P I=0 IFS=''
while read -rd $'\0' C; do
CHARS[I++]=$C
done < <(echo -ne "$(printf '\\x%x\\x00' "${RANGE[#]}")")
for (( I = 0; I < TIMES; ++I )); do
C=${CHARS[RANDOM % RANGE_LENGTH]}
P=$(( RANDOM % STRING_LENGTH ))
STRING="${STRING:0:P}${C}${STRING:P + 1}"
done
echo "$STRING"
# Uncomment to save the value to variable __.
# __=$STRING
}
corrupt "/6435045045/hpdqbhflyuhv_EOlXG" 10 {32..126}
Note: Increase the number (times) to a higher value to get more devastating results.

Resources