我正在尝试从文件中读取字符串的子字符串并将其放入变量中。该字符串看起来如下所示:

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

以下操作成功了,但是对我来说太笨重了:

#!/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

  • 请解释awk(您使用的标签之一)与您的问题有何关联


    – 

  • 1
    awk -F '=|,' '{print $2} ' <<< "dn: uid=myUid,ou=People,dc=mydomain,dc=furtherdomain"


    – 

  • @Cyrus 我相信 DN 中的对的顺序不是固定的,所以如果你运气不好,$2可能会引用与预期不同的值 –


    – 

  • 有一些关于健壮解析要求的有趣信息


    – 

  • @jhnc:在这种情况下,您可以使用 GNU awk 进行如下操作:awk -v FPAT='uid=[^,]+' '{split($1,array,"="); print array[2]}' <<< "dn: uid=myUid,ou=People,dc=mydomain,dc=furtherdomain"


    – 



5 个回答
5

通过bash使用extglob(扩展的 glob),您只需一步即可实现此目的:

# 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

用于%%匹配最长的后缀,而不是最短的后缀,*

$ 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

解析逗号分隔的行

假设参数以这种形式提交:

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

甚至

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

(是的,我呈现她的方式将支持空间!;-)

uid按要求仅提取一个变量 (

类似于:

#!/bin/bash

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

或者,受到uid=的启发,即使不在第一位也能够找到。

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

将所有变量提取到关联数组中。

在下面您可以使用关联数组来索引所有值:

警告:有些字段重复!例如dc出现了两次!为此,我使用逗号,将值分隔到同一字段中。

#!/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#*: }")

然后

echo ${IdArray[uid]}

你的变量将如下所示:

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

或者

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

通过函数将所有内容提取到关联数组中。

#!/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

会产生类似这样的结果:

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

  • 我刚刚意识到某些字段可能出现多次!我编辑了我的答案并添加了一个函数


    – 


grep如果您启用 PCRE ( -P) 以便能够在uid=中捕获属性名称 ( ) ,然后匹配属性值并仅输出匹配的子字符串 ( ),则可以通过一次调用执行此操作:-o

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

[a-zA-Z]+如果您的 ID 不仅仅包含字母,您可以用不同的字符集替换。

使用 sed 可以实现相同的效果,无需 PCRE 后视——我们将预期uid值包装在匹配组中,并将整个字符串替换为仅该\1匹配组:

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

3

  • -P在 Linux 上很常见;但在其他平台上却不常见。


    – 

  • 或者grep -oP 'uid=\K[^,]+。你的 sed 将打印不匹配的全部行:sed -nE 's/.*uid=([^,]+).*/\1/p


    – 

  • @tripleee 是的,这就是为什么我建议所有 macOS 用户首先安装所有 GNU 实用程序 😉 以获得所有新功能(bash、coreutils、diffutils、findutils、gnu-sed、grep)


    –