Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Put a substring of a string into a variable

Tags:

bash

sed

awk

I'm trying to take a substring of a string read from a file and put it in a variable. The string will look something like this:

"dn: uid=myUid,ou=People,dc=mydomain,dc=furtherdomain"

The following succeeds but is too clunky for me:

#!/bin/bash

while IFS= read -r line; do
    if [[ "$line" == *"uid="* ]]; then
        uid1=${line#*uid=}
        uid2=${uid1%,*}
        uid3=${uid2%,*}
        uid=${uid3%,*}
        echo "$uid"
    fi
done <<< "dn: uid=myUid,ou=People,dc=mydomain,dc=furtherdomain"

like image 840
Eric Fetzer Avatar asked Dec 21 '25 06:12

Eric Fetzer


2 Answers

In bash using extglob (extended glob) you can achieve this in a single step:

# enable extended glob
shopt -s extglob

# our input
s="dn: uid=myUid,ou=People,dc=mydomain,dc=furtherdomain"

# cleanup the string using string substitution and declare the variable
declare "${s//+(,*|* )}"

# print the new variable
echo "$uid"

myUid
like image 65
anubhava Avatar answered Dec 23 '25 23:12

anubhava


Parsing comma-separated line

Assuming arguments are submitted in this form:

dn: uid=myUid,ou=People,dc=mydomain,dc=furtherdomain

or even

dn: ou=People,uid=myUid,dc=mydomain,dc=furtherdomain
dn: ou=People,dc=furtherdomain,uid=myUid,dc=mydomain
dn: uid=myUid,ou=People,dc=mydomain,dc=furtherdomain,cn=commonname
dn: fn=Full Name,uid=myUid,ou=People,dc=mydomain,dc=furtherdomain

(Yes, the way I present her will support spaces! ;-)

Extracting only one variable (uid) as requested

Something like:

#!/bin/bash

IdLine="$*"
while IFS== read -d, name val;do
    case $name in
        uid ) uid=$val ;;
    esac
done < <(printf %s "${IdLine#*: }")

Or, inspired by anubhava's answer, but able to find uid= even if not at 1st position.

shopt -s extglob
line='dn: fn=Full Name,uid=myUid,ou=People,dc=mydomain,dc=furtherdomain'
uid="${line//+(*uid=|,*)}"

Extracting all subvariable into an associative array.

Under bash you coud use associative array to index all values:

Warning: There are some field repetition! For sample dc are present two time!! For this I use a coma , to separate values into same field.

#!/bin/bash

IdLine="$*"
declare -A IdArray='()'
while IFS== read -d, name val;do
    if [[ -v IdArray[$name] ]]; then
        IdArray[$name]+=",$val"
    else
        IdArray[$name]="$val"
    fi
done < <(printf %s "${IdLine#*: }")

Then

echo ${IdArray[uid]}

You variable will look like:

declare -p IdArray
declare -A IdArray=([dc]="mydomain,furtherdomain" [ou]="People" [uid]="myUid" )

or

declare -A IdArray=([dc]="mydomain,furtherdomain" [fn]="Full Name" [ou]="People" [uid]="myUid" )

Extracting all to an associative array by a function.

parseDnLine() {
    local -n _aArray=$1
    shift
    local _idLine="$*" _nam _val
    while IFS== read -d, _nam _val; do
        if [[ -v _aArray[$_nam] ]]; then
            _aArray[$_nam]+=,${_val%$'\n'}
        else
            _aArray[$_nam]=${_val%$'\n'}
        fi
    done <<<"${_idLine#*: },"
}

Then prepare 4 associative array:

declare -A test{1..4} # Require to declare Associative Array outside of function

parseDnLine test1 dn: ou=People,uid=myUid,dc=mydomain,dc=furtherdomain

parseDnLine test2 dn: ou=People,dc=furtherdomain,uid=myUid,dc=mydomain

line='dn: uid=myUid,ou=People,dc=mydomain,dc=furtherdomain,cn=commonname'
parseDnLine test3 "$line"

line="dn: fn=Full Name,uid=myUid,ou=People,dc=mydomain,dc=furtherdomain"
parseDnLine test4 $line
declare -p test{1..4}

Will produce something like:

declare -A test1=([dc]="mydomain,furtherdomain" [ou]="People" [uid]="myUid" )
declare -A test2=([dc]="furtherdomain,mydomain" [ou]="People" [uid]="myUid" )
declare -A test3=([dc]="mydomain,furtherdomain" [cn]="commonname" [ou]="People" [uid]="myUid" )
declare -A test4=([dc]="mydomain,furtherdomain" [fn]="Full Name" [ou]="People" [uid]="myUid" )

Then, of course:

echo ${test1[uid]}
myUid

New version suitable for x509 SSL Cert's DN: subject or issuer

With some little edits, this function become suitable for parsing DN lines from X509 certificate files, by using

  • openssl x509 -noout -subject to print subject DN.
  • openssl x509 -noout -issuer to print issuer DN.
parseDnLine() {
    local -n _aArray=$1
    shift
    local _idLine _nam _val _unquot
    IFS=':=' read -r _ _idLine <<<"$*"
    while IFS='=' read -rd, _nam _val; do
    read -r _nam <<<"$_nam"
    read -r _val <<<"${_val%$'\n'}"
    _unquot=${_val//\"}
    while (( ( ${#_val} - ${#_unquot} ) % 2)); do
        read -rd, _unquot
        _val+=", $_unquot"
        _unquot=${_val//\"}
    done
    printf -v _unquot '%b' "${_unquot//\\/\\x}"
    if [[ -v _aArray[$_nam] ]]; then
        _aArray["$_nam"]+=,$_unquot
    else
        _aArray["$_nam"]=$_unquot
    fi
    done <<<"$_idLine,"
}

Then

declare -A 'test=()'
parseDnLine test dn: uid=myUid,ou=People,dc=mydomain,dc=furtherdomain,cn=commonname
declare -p test

Still work:

declare -A test=([dc]="mydomain,furtherdomain" [cn]="commonname" [ou]="People" [uid]="myUid" )

From my local /etc/ssl/certs directory:

declare -A 'test=()'
parseDnLine test $(
    openssl x509 -in /etc/ssl/certs/Go_Daddy_Root_Certificate_Authority_-_G2.pem \
        -noout -subject)
declare -p test
declare -A test=([O]="GoDaddy.com, Inc." [L]="Scottsdale" [C]="US" [ST]="Arizona" [CN]="Go Daddy Root Certificate Authority - G2" )

Note the quote in [O]="GoDaddy.com, Inc.".

And from any website:

declare -A 'test=()'
parseDnLine test $(
     openssl x509 -noout -subject < <(
         openssl s_client <<<$'HEAD / HTTP/1.0\r\n\r' 2>&1 paypal.com:443
))
declare -p test
declare -A test=([jurisdictionC]="US" [O]="PayPal, Inc." [L]="San Jose" [C]="US" [jurisdictionST]="Delaware" [businessCategory]="Private Organization" [ST]="California" [serialNumber]="3014267" [CN]="paypal.com" )

Then, using dunpArray from Looping over arrays, printing both index and value:

dumpArray test 
jurisdictionC   : US
O               : PayPal, Inc.
L               : San Jose
C               : US
jurisdictionST  : Delaware
businessCategory: Private Organization
ST              : California
serialNumber    : 3014267
CN              : paypal.com

Finally print both issuer and subject in one operation:

while read -r line; do
    certyp=${line%%=*};
    echo "-- ${certyp^^} --";
    declare -A 'test=()';
    parseDnLine test "$line";
    dumpArray test;
done < <(openssl x509 -noout -subject -issuer < <(
             openssl s_client google.com:443 <<<$'HEAD / HTTP/1.0\r\n\r' 2>&1))
-- SUBJECT --
CN: *.google.com
-- ISSUER --
O : Google Trust Services
C : US
CN: WR2
like image 45
F. Hauri Avatar answered Dec 23 '25 21:12

F. Hauri



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!