Put a substring of a string into a variable

  Kiến thức lập trình

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"

7

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

Use %% to match the longest, rather than the shortest, suffix that matches ,*.

$ echo ${uid1}
myUid,ou=People,dc=mydomain,dc=furtherdomain
$ echo ${uid1%,*}
myUid,ou=People,dc=mydomain
$ echo ${uid1%%,*}
myUid

$ while IFS= read -r line; do
    if [[ $line =~ uid=([^,]+) ]]; then
        echo "${BASH_REMATCH[1]}"
    fi
done <<< "dn: uid=myUid,ou=People,dc=mydomain,dc=furtherdomain"
myUid

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.

#!/bin/bash

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#*: },"
}

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

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

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

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

line="dn: fn=Full Name,uid=myUid,ou=People,dc=mydomain,dc=furtherdomain"
parseDnLine test4 $line
declare -p test4

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" )

1

You can do this with a single grep call if you enable PCRE (-P) to be able to catch the property name (uid=) in a lookbehind, then match the property value and output only the matched substring (-o):

grep -oP '(?<=uid=)[a-zA-Z]+' <<< 'dn: uid=myUid,ou=People,dc=mydomain,dc=furtherdomain'

You can replace [a-zA-Z]+ with a different character set if your id can contain more than just letters.

Same can be achieved with sed, without a need for PCRE lookbehind — we wrap the expected uid value in a match group, and replace the whole string to only that 1 match group:

sed -E 's/^.*uid=([a-zA-Z]+).*$/1/' <<< 'dn: uid=myUid,ou=People,dc=mydomain,dc=furtherdomain'

3

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website Kho Theme wordpress Kho Theme WP Theme WP

LEAVE A COMMENT