Take first 16 character and covert it into hex string - bash

I have UUID, 3abbea88-c77d-11eb-b8bc-0242ac130003 and I want to take first 16 character of this string and want Hexadecimal string of first 16 characters using shell script.
I tried,
code=$(echo -n ${${ID##*:}:0:16} | od -A n -t x1)
HEX_ID=$(echo ${code//[[:blank:]]/})
Any better way ?
Expected Output : 33616262656138382d633737642d3131

Using od you can simply limit the number of read characters using the -N option:
HEX_ID=$(od -A n -t x1 -N 16 <<< ${ID##*:} | tr -dc '[:xdigit:]')
Edit: tr is used to suppress non-hexadecimal characters, namely whitespaces and potential newlines.

Perl to the rescue!
perl -le 'print unpack "H32", shift' 3abbea88-c77d-11eb-b8bc-0242ac130003
-l adds newlines to print
unpack takes a string and expands it to a list of values based on a template. H32 means "take characters and interpret them as 32 hex values".
shift reads the first command line argument.
Or, using xxd and head:
echo 3abbea88-c77d-11eb-b8bc-0242ac130003 | xxd -p | head -c32

That's certainly a useless echo.
Probably avoid uppercase for your private variables.
uuid='3abbea88-c77d-11eb-b8bc-0242ac130003'
tmp=${uuid//-/}
hex_id=$(od -A n -t x1 <<<${tmp:0:13})
hex_id=${hex_id//[[:blank:]]/}
hex_id=${hex_id%0a}
The here string unattractively supplies trailing newline to od which we have to trim off.

Bash-only:
while read -r -N 1 c # read input string 1 char at a time
do [[ "$c" == " " ]] || # skip embedded spaces
printf "%02X" "$( # output the hexidecimal value of
printf "%d" \'$c # the ASCII decimal ordinal of $c
)"
done <<< "${text##*:}" # ignoring the leading trash to the :
echo # newline-teminate the output
All in one line:
while read -rn1 c;do [[ "$c" == " " ]]||printf %02X $(printf "%d" \'$c);done<<<"${text##*:}";echo
This is not the fastest approach...

hexdump does it all:
hexdump -n 16 -ve '1/1 "%.2x"'
-n 16 means only process the first 16 bytes
-e '1/1 "%.2x"' means display each byte using given printf format
-v means display normally (without this, it replaces dupe sections with * đŸ¤·)
echo '3abbea88-c77d-11eb-b8bc-0242ac130003' | hexdump -n 16 -ve '1/1 "%.2x"'
output:
33616262656138382d633737642d3131

Related

Syntax error while trying to generate a 2048bit long prime number

I'm trying to generate a 2048 bit long prime number, this is my code so far:
#!/bin/bash
generate_random() {
hex=$(head -c 256 /dev/urandom | xxd -p)
bc <<< "ibase=16; $hex"
}
p=$(generate_random)
echo "$p"
While running the script I get (standard_in) 1: syntax error followed by random zeroes.
Anyone knows what is causing this error and how can I fix it? I've tried with bash -x, but it doesn't add any useful information.
First, bc understands only upper-case letters as hex digits (at least by default). Second, you have separators in your xxd output, so you generate multiple numbers with bc later.
This should work:
#!/bin/bash
generate_random() {
hex=$(head -c 256 /dev/urandom | xxd -p -u | tr -d '\n')
bc <<< "ibase=16; $hex"
}
p=$(generate_random)
echo "$p"
-u flag to xxd instructs it to output upper-case letters as digits, and tr removes separators.
Example output:
84404284040092528807148386035025161100484110236893077703095592941720\
00537078513504880246726730474236368181068985417211434943913923235822\
01284401417146606673073772989889733010524123703686975444423088406509\
44767677616371794606797386146855833950295071249000795855185540560405\
62673903614333076371092344026999031152809898928396395497832309795471\
93897215963003601022703133486344387720277877558264139632520964120681\
97764906669023878701319760947789227343517474218584987497204300184084\
62846775760153647010072072799120566180042021620262646969602253704108\
06274157727080642084167983313757899766696995668747042179553171962777\
5716
To remove newline separators and backslashes, you can do
p_joined=$(echo "$p" | sed -z 's=\\\n==g')
echo "$p_joined"
instead.
An alternative way might be
printf -v hex '%s' $(od -v -An -N256 -x /dev/urandom)
read dec < <(bc <<< "ibase=16; ${hex^^}")
echo $dec

How to turn a string into a modified hex representation?

i want to turn a string like
AaAa
into
a string like this
%<41>%<61>%<41>%<61>
Simple enough with the programming languages i am familar with, but with bash i can't get get the piping right to do what i am trying to do:
split string into char array
turn each char into hex
wrap each hex value into %<FF>
concat string
this is my current way which gets me half way there:
echo -n "AaAa" | od -A n -t x1
If you are already using od,
printf "%%<%s>" $(od -A n -t x1<<<"AaAa")
For an all-bash without od,
while read -r -N 1 c; do printf "%%<%02X>" "$( printf "%d" \'$c )"; done <<< AaAa
The downside of this approach is that it spawns a subshell for every character, and assumes ASCII/UTF8.
edit
#Shawn pointed out that you don't need the subshell -
while read -r -N 1 c; do printf "%%<%02X>" \'$c; done <<< AaAa
I noticed that these are leaving the string terminator in your output, though, and realized I could eliminate that and the read by assigning the data to a variable and using the built-in parsing tools.
$: x=AaAa && for((i=0;i<${#x};i++)); do printf "%%<%02X>" \'${x:i:1}; done; echo
%<41>%<61>%<41>%<61>
A simple Perl substitution would do the trick:
echo -n AaAa | perl -pe's/(.)/ sprintf "%%<%02X>", ord($1) /seg'
Shorter:
echo -n AaAa | perl -ne'printf "%%<%02X>", $_ for unpack "C*"'
In both cases, the output is the expected
%<41>%<61>%<41>%<61>
(No trailing line feed added. If you want one, append ; END { print "\n" }.)
You can pipe to sed to wrap each byte in %<> and then remove the whitespace.
echo -n "AaAa" | od -A n -t x1 | sed -E -e 's/[a-z0-9]+/%<&>/g' -e 's/ //g'
You could use perl:
echo -n AaAa | perl -ne 'for $c (split//) { printf("%%<%02X>", ord($c)); }'
Output
%<41>%<61>%<41>%<61>
Maybe awk
echo -n "AaAa" |
od -A n -t x1 |
awk 'BEGIN { ORS = "" } { for (i = 1; i <= NF; i+=1) print "%<"$i">"}'

Hex to decimal conversion in bash without using gawk

Input:
cat test1.out
12 , maze|style=0x48570006, column area #=0x7, location=0x80000d
13 , maze|style=0x48570005, column area #=0x7, location=0x80aa0d
....
...
..
.
Output needed:
12 , maze|style=0x48570006, column area #=0x7, location=8388621 <<<8388621 is decimal of 0x80000d
....
I want to convert just the last column to decimal.
I cannot use gawk as it is not available in our company machines everywhere.
Tried using awk --non-decimal-data but it didnt work also.
Wondering if just printf command can work on flipping the last word from hex to decimal.
Any other ideas that you can suggest?
There's no need for awk or any other external commands here: bash's native math operation handle hexadecimal values correctly when in an arithmetic context (this is why echo $((0xff)) emits 255).
#!/usr/bin/env bash
# ^^^^- must be really bash, not /bin/sh
location_re='location=(0x[[:xdigit:]]+)([[:space:]]|$)'
while read -r line; do
if [[ $line =~ $location_re ]]; then
hex=${BASH_REMATCH[1]}
dec=$(( $hex ))
printf '%s\n' "${line/location=$hex/location=$dec}"
else
printf '%s\n' "$line"
fi
done
You can see this running at https://ideone.com/uN7qNY
Considering the case strtonum() function is not available, how about:
#!/bin/bash
awk -F'location=0x' '
function hex2dec(str,
i, x, c, tab) {
for (i = 0; i <= 15; i++) {
tab[substr("0123456789ABCDEF", i + 1, 1)] = i;
}
x = 0
for (i = 1; i <= length(str); i++) {
c = toupper(substr(str, i, 1))
x = x * 16 + tab[c]
}
return x
}
{
print $1 "location=" hex2dec($2)
}
' test1.out
where hex2dec() is a homemade substituion of strtonum().
Wait, can't you just use printf in other awks? It won't work with gawk but it does with other awks, right? For example with mawk:
$ mawk 'BEGIN{FS=OFS="="}{$NF=sprintf("%d", $NF);print}' file
12 , maze|style=0x48570006, column area #=0x7, location=8388621
13 , maze|style=0x48570005, column area #=0x7, location=8432141
I tested with mawk, awk-20070501, awk-20121220 and Busybox awk.
Discarded after edit but left for comments' sake:
Using rev and cut to extract around the last = and printf for hex2dec conversion:
$ while IFS='' read -r line || [[ -n "$line" ]]
do
printf "%s=%d\n" "$(echo "$line" | rev | cut -d = -f 2- | rev)" \
$(echo "$line" | rev | cut -d = -f 1 | rev)
done < file
Output:
12 , maze|style=0x48570006, column area #=0x7, location=8388621
13 , maze|style=0x48570005, column area #=0x7, location=8432141
If you have Perl installed, not having Gawk is rather inconsequential.
perl -pe 's/location=\K0x([0-9a-f]+)/ hex($1) /e' file
This might work for you (GNU sed and Bash):
sed 's/\(.*location=\)\(0x[0-9a-f]\+\)/echo "\1$((\2))"/Ie' file
Use pattern matching and back references to split each line and then evaluate an echo command.
Alternative:
sed 's/\(.*location=\)\(0x[0-9a-f]\+\)/echo "\1$((\2))"/I' file | sh
BASH_REMATCH array info :
http://molk.ch/tips/gnu/bash/rematch.html
quintessential principe :
[[ string =~ regexp ]]
[[ "abcdef" =~ (b)(.)(d)e ]]
If the 'string' matches 'regexp',
.. the matched part of the string is stored in the BASH_REMATCH array.
# Now:
# BASH_REMATCH[0]=bcde # as the total match
# BASH_REMATCH[1]=b # as the 1'th captured group
# BASH_REMATCH[2]=c # as ...
# BASH_REMATCH[3]=d
enjoy !
Bash's native math operation handles hexadecimal values correctly anytime.
Example:
echo $(( 0xff))
255
printf '%d' 0xf0
240

Remove leading zeros from MAC address

I have a MAC address that looks like this.
01:AA:BB:0C:D0:E1
I want to convert it to lowercase and strip the leading zeros.
1:aa:bb:c:d0:e1
What's the simplest way to do that in a Bash script?
$ echo 01:AA:BB:0C:D0:E1 | sed 's/\(^\|:\)0/\1/g;s/.*/\L\0/'
1:aa:bb:c:d0:e1
\(^\|:\)0 represents either the line start (^) or a :, followed by a 0.
We want to replace this by the capture (either line start or :), which removed the 0.
Then, a second substitution (s/.*/\L\0/) put the whole line in lowercase.
$ sed --version | head -1
sed (GNU sed) 4.2.2
EDIT: Alternatively:
echo 01:AA:BB:0C:D0:E1 | sed 's/0\([0-9A-Fa-f]\)/\1/g;s/.*/\L\0/'
This replaces 0x (with x any hexa digit) by x.
EDIT: if your sed does not support \L, use tr:
echo 01:AA:BB:0C:D0:E1 | sed 's/0\([0-9A-Fa-f]\)/\1/g' | tr '[:upper:]' '[:lower:]'
Here's a pure Bash≥4 possibility:
mac=01:AA:BB:0C:D0:E1
IFS=: read -r -d '' -a macary < <(printf '%s:\0' "$mac")
macary=( "${macary[#]#0}" )
macary=( "${macary[#],,}" )
IFS=: eval 'newmac="${macary[*]}"'
The line IFS=: read -r -d '' -a macary < <(printf '%s:\0' "$mac") is the canonical way to split a string into an array,
the expansion "${macary[#]#0}" is that of the array macary with leading 0 (if any) removed,
the expansion "${macary[#],,}" is that of the array macary in lowercase,
IFS=: eval 'newmac="${macary[*]}"' is a standard way to join the fields of an array (note that the use of eval is perfectly safe).
After that:
declare -p newmac
yields
declare -- newmac="1:aa:bb:c:d0:e1"
as required.
A more robust way is to validate the MAC address first:
mac=01:AA:BB:0C:D0:E1
a='([[:xdigit:]]{2})' ; regex="^$a:$a:$a:$a:$a:$a$"
[[ $mac =~ $regex ]] || { echo "Invalid MAC address" >&2; exit 1; }
And then, using the valid result of the regex match (BASH_REMATCH):
set -- $(printf '%x ' $(printf '0x%s ' "${BASH_REMATCH[#]:1}" ))
IFS=: eval 'printf "%s\n" "$*"'
Which will print:
1:aa:bb:c:d0:e1
Hex values without leading zeros and in lowercase.
If Uppercase is needed, change the printf '%x ' to printf '%X '.
If Leading zeros are needed change the same to printf '%02x '.

How to shift each letter of the string by a given number of letters?

How can i shift each letter of a string by a given number of letters down or up in bash, without using a hardcoded dictionary?
Do you mean something like ROT13:
pax$ echo 'hello there' | tr '[a-z]' '[n-za-m]'
uryyb gurer
pax$ echo 'hello there' | tr '[a-z]' '[n-za-m]' | tr '[a-z]' '[n-za-m]'
hello there
For a more general solution where you want to provide an arbitrary rotation (0 through 26), you can use:
#!/usr/bin/bash
dual=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
phrase='hello there'
rotat=13
newphrase=$(echo $phrase | tr "${dual:0:26}" "${dual:${rotat}:26}")
echo ${newphrase}
If you want to rotate also the capitals you could use something like this:
cat data.txt | tr '[a-z]' '[n-za-m]' | tr '[A-Z]' '[N-ZA-M]'
where data.txt has whatever you want to rotate.
$ alpha=abcdefghijklmnopqrstuvwxyz
$ rot=3
$ sed "y/${alpha}/${alpha:$rot}${alpha::$rot}/" <<< 'foobar'
irredu
Shift by 12 characters(A becomes M, and vice versa)
Encryption
----------
$> echo ABCDE | tr '[A-Z]' '[M-ZA-L]' // prints MNOPQ
Decryption
----------
$> echo MNOPQ | tr '[M-ZA-L]' '[A-Z]' // prints ABCDE
In the encryption example, we are piping ABCDE to the command tr which is given two arguments. The first one is a matching string. It will match certain strings in your input(in our case ABCDE). The second argument works upon the result of the first argument and modifies it accordingly. So, we're basically matching any uppercase letter present in the input ABCDE and passing it to the second argument. The second argument replaces the characters with their 12th next counterpart. Now, this part is important to understand and might confuse some people, we're basically going from [M-L] in the second argument. Since the tr command doesn't accept this directly, we're breaking it up into two separate chunks. First chunk is [M-Z] and the second one is [A-L]. It's basically like a search-and-replace mechanism. You search with the first argument, modify with the second argument, as simple as that.
For the second example, I've just swapped the first argument with the second one in the tr command. Which acts perfectly as a decryptor. You could write it the same way as the first example, but I find it less time consuming when I have the encryption algorithm and I can just swap the arguments to have a decryption algorithm as well.
Or
cat data.txt | tr 'a-zA-Z' 'n-za-mN-ZA-M'
It will also work
Without using tr, shift 1 to 25 characters
and can be decrypted using 26 - original key
#!/bin/bash
#set -x
i=0
for letters in {A..Z}
do
abc_cap[$i]="$letters"
((i++))
done
i=0
for letters in {a..z}
do
abc_small[$i]="$letters"
((i++))
done
read -r -p "Enter message to be encrypted/decrypted: " -a message
read -r -p "Enter shift amount (26 - orig key for decrypt): " shift_amount
echo -n "Encrypted message: "
if [ "$shift_amount" -gt 25 ] || [ "$shift_amount" -lt 1 ]
then
echo "Shift amount out of range"
exit
fi
for word in "${message[#]}"
do
while read -r -n 1 letter
do
if [[ "$letter" = [a-z] ]]
then
for a in "${!abc_small[#]}"
do
if [ "${abc_small[$a]}" = "$letter" ]
then
a=$(echo "($a + $shift_amount) % 26" | bc)
echo -n "${abc_small[$a]}"
fi
done
elif [[ "$letter" = [A-Z] ]]
then
for a in "${!abc_cap[#]}"
do
if [ "${abc_cap[$a]}" = "$letter" ]
then
a=$(echo "($a + $shift_amount) % 26" | bc)
echo -n "${abc_cap[$a]}"
fi
done
elif [[ "$letter" = "" ]]
then echo -n " "
else echo -n "$letter"
fi
done < <(echo "$word")
done
echo
exit
Problem statement and how this command can help you:
For example The password is stored in the file data.txt, where 13 positions have rotated all lowercase (a-z) and uppercase (A-Z) letters.
The data.txt file contains 1 line encrypted with the ROT13 ( rotation by 13) algorithm. In order to decrypt it, I have to replace every letter with the letter 13 positions ahead.
file contains the data as shown below
cat data.txt
Gur cnffjbeq vf WIAOOSFzMjXXBC0KoSKBbJ8puQm5lIEi
after rotation to 13 character, the password will look like this.
The password is JVNBBFSmZwKKOP0XbFXOoW8chDz5yVRv
The command to Do that is given below.
cat data.txt | tr '[A-Za-z]' '[N-ZA-Mn-za-m]'
Explanation of the Command
cat data.txt read all the character in data.txt file and then pass to tr command, tr commands takes two arguments, the first argument [A-Za-z] read only the characters made of A-Z or a-z. and in the second argument is rotation regular expression.
[13th character from A - ZA-12th character from A and same expression as for small letters]
[N-ZA-Mn-za-m]
N : 13th character from A.
Z : to the end.
A : first character.
N : just a previous character from the 13th character. to complete the circle.
repeat the same expression for small letters.
We rotated by 13, you can replace the 13th and Previous character by any x position to rotate the string by x characters

Resources